diff --git a/schnorr_fun/src/adaptor/mod.rs b/schnorr_fun/src/adaptor/mod.rs
index e1714fe6..ba9893b7 100644
--- a/schnorr_fun/src/adaptor/mod.rs
+++ b/schnorr_fun/src/adaptor/mod.rs
@@ -54,7 +54,7 @@ use crate::{
         g,
         marker::*,
         nonce::NonceGen,
-        s, Point, Scalar,
+        s, Point, Scalar, G,
     },
     KeyPair, Message, Schnorr, Signature,
 };
@@ -78,7 +78,7 @@ pub trait EncryptedSign {
     ) -> EncryptedSignature;
 }
 
-impl<NG, CH, GT> EncryptedSign for Schnorr<CH, NG, GT>
+impl<NG, CH> EncryptedSign for Schnorr<CH, NG>
 where
     CH: Digest<OutputSize = U32> + Clone,
     NG: NonceGen,
@@ -98,7 +98,7 @@ where
             public => [X, Y, message]
         );
 
-        let R = g!(r * { self.G() } + Y)
+        let R = g!(r * G + Y)
             // R_hat = r * G is sampled pseudorandomly for every Y which means R_hat + Y is also
             // be pseudoranodm and therefore will not be zero.
             // NOTE: Crucially we add Y to the nonce derivation to ensure this is true.
@@ -110,7 +110,7 @@ where
         // key before decrypting it
         r.conditional_negate(needs_negation);
 
-        let c = self.challenge(&R.to_xonly(), X, message);
+        let c = self.challenge(R.to_xonly(), X, message);
         let s_hat = s!(r + c * x).mark::<Public>();
 
         EncryptedSignature {
@@ -188,12 +188,12 @@ pub trait Adaptor {
     ) -> Option<Scalar>;
 }
 
-impl<CH, NG, GT> Adaptor for Schnorr<CH, NG, GT>
+impl<CH, NG> Adaptor for Schnorr<CH, NG>
 where
     CH: Digest<OutputSize = U32> + Clone,
 {
     fn encryption_key_for(&self, decryption_key: &Scalar) -> Point {
-        g!(decryption_key * { self.G() }).normalize()
+        g!(decryption_key * G).normalize()
     }
 
     #[must_use]
@@ -216,9 +216,9 @@ where
         // !needs_negation => R_hat = R - Y
         let R_hat = g!(R + { Y.conditional_negate(!needs_negation) });
 
-        let c = self.challenge(&R.to_xonly(), &X.to_xonly(), message);
+        let c = self.challenge(R.to_xonly(), X.to_xonly(), message);
 
-        R_hat == g!(s_hat * { self.G() } - c * X)
+        R_hat == g!(s_hat * G - c * X)
     }
 
     fn decrypt_signature(
@@ -257,7 +257,7 @@ where
 
         let mut y = s!(s - s_hat);
         y.conditional_negate(*needs_negation);
-        let implied_encryption_key = g!(y * { self.G() });
+        let implied_encryption_key = g!(y * G);
 
         if implied_encryption_key == *encryption_key {
             Some(y.expect_nonzero("unreachable - encryption_key is NonZero and y*G equals it"))
@@ -271,48 +271,55 @@ where
 mod test {
 
     use super::*;
-    use crate::nonce::{self, Deterministic};
-    use secp256kfun::TEST_SOUNDNESS;
+    use crate::nonce::{Deterministic, GlobalRng, Synthetic};
+    use rand::rngs::ThreadRng;
+    use secp256kfun::proptest::prelude::*;
+    use sha2::Sha256;
     #[cfg(target_arch = "wasm32")]
     use wasm_bindgen_test::wasm_bindgen_test as test;
 
-    fn test_schnorr<NG: NonceGen>(schnorr: Schnorr<sha2::Sha256, NG>) {
-        for _ in 0..TEST_SOUNDNESS {
-            let signing_keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
-            let verification_key = signing_keypair.verification_key();
-            let decryption_key = Scalar::random(&mut rand::thread_rng());
-            let encryption_key = schnorr.encryption_key_for(&decryption_key);
-            let message = Message::<Public>::plain("test", b"give 100 coins to Bob".as_ref());
-
-            let encrypted_signature =
-                schnorr.encrypted_sign(&signing_keypair, &encryption_key, message);
-
-            assert!(schnorr.verify_encrypted_signature(
-                &signing_keypair.verification_key(),
-                &encryption_key,
-                message,
-                &encrypted_signature,
-            ));
+    proptest! {
+        #[test]
+        fn signing_tests_deterministic(secret_key in any::<Scalar>(), decryption_key in any::<Scalar>()) {
+            let schnorr = Schnorr::<Sha256, Deterministic<Sha256>>::default();
+            test_it(schnorr, secret_key, decryption_key);
+        }
 
-            let decryption_key = decryption_key.mark::<Public>();
-            let signature =
-                schnorr.decrypt_signature(decryption_key.clone(), encrypted_signature.clone());
-            assert!(schnorr.verify(&verification_key, message, &signature));
-            let rec_decryption_key = schnorr
-                .recover_decryption_key(&encryption_key, &encrypted_signature, &signature)
-                .expect("recovery works");
-            assert_eq!(rec_decryption_key, decryption_key);
+        #[test]
+        fn signing_tests_synthetic(secret_key in any::<Scalar>(), decryption_key in any::<Scalar>()) {
+            let schnorr = Schnorr::<Sha256, Synthetic<Sha256, GlobalRng<ThreadRng>>>::default();
+            test_it(schnorr, secret_key, decryption_key);
         }
+
     }
 
-    #[test]
-    fn sign_plain_message() {
-        use rand::rngs::ThreadRng;
-        use sha2::Sha256;
-        test_schnorr(Schnorr::new(Deterministic::<Sha256>::default()));
-        test_schnorr(Schnorr::new(nonce::Synthetic::<
-            Sha256,
-            nonce::GlobalRng<ThreadRng>,
-        >::default()));
+    fn test_it<NG: NonceGen>(
+        schnorr: Schnorr<Sha256, NG>,
+        secret_key: Scalar,
+        decryption_key: Scalar,
+    ) {
+        let signing_keypair = schnorr.new_keypair(secret_key);
+        let verification_key = signing_keypair.verification_key();
+        let encryption_key = schnorr.encryption_key_for(&decryption_key);
+        let message = Message::<Public>::plain("test", b"give 100 coins to Bob".as_ref());
+
+        let encrypted_signature =
+            schnorr.encrypted_sign(&signing_keypair, &encryption_key, message);
+
+        assert!(schnorr.verify_encrypted_signature(
+            &signing_keypair.verification_key(),
+            &encryption_key,
+            message,
+            &encrypted_signature,
+        ));
+
+        let decryption_key = decryption_key.mark::<Public>();
+        let signature =
+            schnorr.decrypt_signature(decryption_key.clone(), encrypted_signature.clone());
+        assert!(schnorr.verify(&verification_key, message, &signature));
+        let rec_decryption_key = schnorr
+            .recover_decryption_key(&encryption_key, &encrypted_signature, &signature)
+            .expect("recovery works");
+        assert_eq!(rec_decryption_key, decryption_key);
     }
 }
diff --git a/schnorr_fun/src/keypair.rs b/schnorr_fun/src/keypair.rs
index d8bf91af..6693ce87 100644
--- a/schnorr_fun/src/keypair.rs
+++ b/schnorr_fun/src/keypair.rs
@@ -15,7 +15,7 @@ use secp256kfun::{marker::*, Point, Scalar, XOnly};
 /// ```
 ///
 /// [`Schnorr`]: crate::Schnorr
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq)]
 pub struct KeyPair {
     pub(crate) sk: Scalar,
     pub(crate) pk: XOnly,
@@ -27,9 +27,9 @@ impl KeyPair {
         &self.sk
     }
 
-    /// Returns a reference to the public key.
-    pub fn public_key(&self) -> &XOnly {
-        &self.pk
+    /// The public key
+    pub fn public_key(&self) -> XOnly {
+        self.pk
     }
 
     /// Gets a reference to the key-pair as a tuple
@@ -39,8 +39,8 @@ impl KeyPair {
     /// # use schnorr_fun::{Schnorr, fun::Scalar};
     /// # let keypair = schnorr_fun::test_instance!().new_keypair(Scalar::one());
     /// let (secret_key, public_key) = keypair.as_tuple();
-    pub fn as_tuple(&self) -> (&Scalar, &XOnly) {
-        (&self.sk, &self.pk)
+    pub fn as_tuple(&self) -> (&Scalar, XOnly) {
+        (&self.sk, self.pk)
     }
 
     /// Returns the full `Point<EvenY>` for the public key which is used in [`verify`].
diff --git a/schnorr_fun/src/lib.rs b/schnorr_fun/src/lib.rs
index 6b0caca4..2a4cc02d 100755
--- a/schnorr_fun/src/lib.rs
+++ b/schnorr_fun/src/lib.rs
@@ -2,20 +2,30 @@
 #![no_std]
 #![allow(non_snake_case)]
 #![doc = include_str!("../README.md")]
-#![deny(warnings, missing_docs)]
+#![warn(missing_docs)]
+
 #[cfg(all(feature = "alloc", not(feature = "std")))]
 #[macro_use]
 extern crate alloc;
+#[cfg(all(feature = "alloc", not(feature = "std")))]
+pub(crate) use alloc::vec::Vec;
 
 #[cfg(feature = "std")]
 #[macro_use]
 extern crate std;
+#[cfg(feature = "std")]
+pub(crate) use std::vec::Vec;
 
 #[cfg(feature = "serde")]
 extern crate serde_crate as serde;
 
 pub use secp256kfun as fun;
 pub use secp256kfun::nonce;
+
+// musig needs vecs
+#[cfg(feature = "alloc")]
+pub mod musig;
+
 mod signature;
 pub use signature::Signature;
 pub mod adaptor;
diff --git a/schnorr_fun/src/message.rs b/schnorr_fun/src/message.rs
index cd313043..b35765fe 100644
--- a/schnorr_fun/src/message.rs
+++ b/schnorr_fun/src/message.rs
@@ -29,7 +29,7 @@ impl<'a, 'b, S: Secrecy> Message<'a, S> {
     ///
     /// [here]: https://github.com/sipa/bips/issues/207#issuecomment-673681901
     pub fn plain(app_tag: &'static str, bytes: &'a [u8]) -> Self {
-        assert!(app_tag.len() <= 64, "tag must not be 64 bytes or less");
+        assert!(app_tag.len() <= 64, "tag must be 64 bytes or less");
         assert!(!app_tag.is_empty(), "tag must not be empty");
         Message {
             bytes: bytes.mark::<S>(),
@@ -39,7 +39,7 @@ impl<'a, 'b, S: Secrecy> Message<'a, S> {
 }
 
 impl<S> HashInto for Message<'_, S> {
-    fn hash_into(&self, hash: &mut impl Digest) {
+    fn hash_into(self, hash: &mut impl Digest) {
         if let Some(prefix) = self.app_tag {
             let mut padded_prefix = [0u8; 64];
             padded_prefix[..prefix.len()].copy_from_slice(prefix.as_bytes());
diff --git a/schnorr_fun/src/musig.rs b/schnorr_fun/src/musig.rs
new file mode 100644
index 00000000..4dd93bc8
--- /dev/null
+++ b/schnorr_fun/src/musig.rs
@@ -0,0 +1,1325 @@
+//! The MuSig2 multisignature scheme.
+//!
+//! ## Synopsis
+//!
+//! ```
+//! use schnorr_fun::{musig::{MuSig, Party}, Schnorr, Message, nonce::Deterministic};
+//! use sha2::Sha256;
+//! // use sha256 with deterministic nonce generation
+//! let musig = MuSig::<Sha256, Schnorr<Sha256, Deterministic<Sha256>>>::default();
+//! // create a keylist
+//! # use schnorr_fun::fun::Scalar;
+//! # let kp1 = musig.schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
+//! # let kp3 = musig.schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
+//! # let p1_pubkey = kp1.public_key();
+//! # let p3_pubkey = kp3.public_key();
+//! # let my_keypair = musig.schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
+//! # let _keylist = musig.new_keylist(vec![
+//! #     Party::Local(kp1),
+//! #     Party::Remote(my_keypair.public_key()),
+//! #     Party::Local(kp3),
+//! # ]);
+//! let keylist = musig.new_keylist(vec![
+//!     Party::Remote(p1_pubkey),
+//!     Party::Local(my_keypair),
+//!     Party::Remote(p3_pubkey),
+//! ]);
+//! let message = Message::plain("my-app", b"chancellor on brink of second bailout for banks");
+//! // generate our aggregate key
+//! let agg_key = keylist.agg_public_key();
+//! // start a MuSig2 session by first exchanging nonces.
+//! // Since we're using deterministic nonces it's important we only use the session id once
+//! let my_nonces = musig.gen_nonces(&keylist, b"session-id-1337");
+//! // send this to the other parties
+//! let my_public_nonce = my_nonces[0].public();
+//! # let nonces = musig.gen_nonces(&_keylist, b"session-id-1337");
+//! # let p1_nonce = nonces[0].public();
+//! # let p3_nonce = nonces[1].public();
+//! # let mut _session = musig.start_sign_session_deterministic(&_keylist, my_nonces.iter().map(|n| n.public()), b"session-id-1337", message).unwrap();
+//! // Once you've got the nonces from the other two (p1_nonce and p3_nonce) you can start the signing session.
+//! let mut session = musig.start_sign_session(&keylist, my_nonces, [p1_nonce, p3_nonce], message).unwrap();
+//! // but since we're using deterministic nonce generation we can just remember the session id.
+//! // You should guarantee that this is not called ever again with the same session id!!!!
+//! let mut session = musig.start_sign_session_deterministic(&keylist, [p1_nonce, p3_nonce], b"session-id-1337", message).unwrap();
+//! // sign with our (single) local keypair
+//! let my_sig = musig.sign_all(&keylist, &mut session)[0];
+//! # let _sigs = musig.sign_all(&_keylist, &mut _session);
+//! # let p1_sig = _sigs[0];
+//! # let p3_sig = _sigs[1];
+//! // receive p1_sig and p3_sig from somewhere and check they're valid
+//! assert!(musig.verify_partial_signature(&keylist, &session, 0, p1_sig));
+//! assert!(musig.verify_partial_signature(&keylist, &session, 2, p3_sig));
+//! // combine them with ours into the final signature
+//! let sig = musig.combine_partial_signatures(&keylist, &session, [my_sig, p1_sig, p3_sig]);
+//! // check it's a valid normal Schnorr signature
+//! musig.schnorr.verify(&keylist.agg_verification_key(), message, &sig);
+//! ```
+//!
+//! ## Description
+//!
+//! The MuSig2 multisignature scheme lets you aggregate multiple public keys into a single public
+//! key that requires all of the corresponding secret keys to authorize a signature under the aggregate key.
+//!
+//! This implementation is protocol compatible with the implementation merged into
+//! [secp256k1-zkp].
+//!
+//! See [the excellent paper] for the abstract details of the protocol.
+//!
+//! [the excellent paper]: https://eprint.iacr.org/2020/1261.pdf
+//! [secp256k1-zkp]: https://github.com/ElementsProject/secp256k1-zkp/pull/131
+use crate::{adaptor::EncryptedSignature, KeyPair, Message, Schnorr, Signature, Vec};
+use secp256kfun::{
+    derive_nonce,
+    digest::{generic_array::typenum::U32, Digest},
+    g,
+    hash::{HashAdd, Tagged},
+    marker::*,
+    nonce::{Deterministic, NonceGen},
+    s, Point, Scalar, XOnly, G,
+};
+
+/// The MuSig context.
+pub struct MuSig<H, S = ()> {
+    /// The hash used to compress the key list to 32 bytes.
+    pub pk_hash: H,
+    /// The hash used to generate each key's coefficient.
+    pub coeff_hash: H,
+    /// The hash used to generate the nonce coefficients.
+    pub nonce_coeff_hash: H,
+    /// The instance of the underlying Schnorr context.
+    pub schnorr: S,
+}
+
+impl<H: Tagged, S> MuSig<H, S> {
+    fn _new(schnorr: S) -> Self {
+        Self {
+            pk_hash: H::default().tagged(b"KeyAgg list"),
+            coeff_hash: H::default().tagged(b"KeyAgg coefficient"),
+            nonce_coeff_hash: H::default().tagged(b"MuSig/noncecoef"),
+            schnorr,
+        }
+    }
+}
+
+impl<H: Tagged, S: Default> Default for MuSig<H, S> {
+    fn default() -> Self {
+        MuSig::_new(S::default())
+    }
+}
+
+impl<H: Tagged> MuSig<H, ()> {
+    /// Creates a MuSig context that can only do key aggregation.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use schnorr_fun::fun::{ XOnly };
+    /// # let key1 = XOnly::random(&mut rand::thread_rng());
+    /// # let key2 = XOnly::random(&mut rand::thread_rng());
+    /// use schnorr_fun::musig::{MuSig, Party};
+    /// use sha2::Sha256;
+    /// let musig = MuSig::<Sha256>::keyagg_only();
+    /// let keylist = musig.new_keylist(vec![Party::Remote(key1), Party::Remote(key2)]);
+    /// println!("{:?}", keylist.agg_public_key())
+    /// ```
+    pub fn keyagg_only() -> Self {
+        Self::_new(())
+    }
+}
+
+impl<H: Tagged, NG> MuSig<H, Schnorr<H, NG>> {
+    /// Generate a new MuSig context from a Schnorr context.
+    pub fn new(schnorr: Schnorr<H, NG>) -> Self {
+        Self::_new(schnorr)
+    }
+}
+
+/// A party in the protocol.
+///
+/// A party is either local (we know the secret key) or remote (we only know the public key).
+#[derive(Debug, Clone)]
+pub enum Party {
+    /// A local party (we have the keypair)
+    Local(KeyPair),
+    /// A remote party (we only know the public key)
+    Remote(XOnly),
+}
+
+/// A list of keys aggregated into a single key.
+///
+/// Created using [`MuSig::new_keylist`].
+///
+/// The `KeyList` can't be serialized but it's very efficient to re-create it from the initial list of keys.
+///
+/// [`MuSig::new_keylist`]
+#[derive(Debug, Clone)]
+pub struct KeyList {
+    /// The parties involved in the key aggregation.
+    parties: Vec<Party>,
+    /// The coefficients of each key
+    coefs: Vec<Scalar<Public>>,
+    /// The aggregate key
+    agg_key: Point<EvenY>,
+    /// The
+    tweak: Scalar<Public, Zero>,
+    needs_negation: bool,
+}
+
+impl KeyList {
+    /// The `XOnly` aggregated key for the keylist.
+    pub fn agg_public_key(&self) -> XOnly {
+        self.agg_key.to_xonly()
+    }
+    /// The aggregated key for the keylist as a `Point`.
+    pub fn agg_verification_key(&self) -> Point<EvenY> {
+        self.agg_key
+    }
+    /// An iterator over the **public keys** of each party in the keylist.
+    pub fn keys(&self) -> impl Iterator<Item = XOnly> + '_ {
+        self.parties.iter().map(|party| match party {
+            Party::Local(keypair) => keypair.public_key(),
+            Party::Remote(xonly) => *xonly,
+        })
+    }
+
+    /// Returns an iterator over the parties
+    pub fn parties(&self) -> impl Iterator<Item = &Party> {
+        self.parties.iter()
+    }
+
+    /// Clear all secret keys from the parties.
+    ///
+    /// i.e. convert all [`Party::Local`] into [`Party::Remote`]
+    pub fn clear_secrets(self) -> KeyList {
+        Self {
+            parties: self.keys().map(|key| Party::Remote(key)).collect(),
+            ..self
+        }
+    }
+
+    /// *Tweak* the aggregated key with a scalar so that the resulting key is equal to the existing
+    /// key plus `tweak * G`. The tweak mutates the public key while still allowing the original set
+    /// of signers to sign under the new key.
+    ///
+    /// This is how you embed a taproot commitment into a key.
+    ///
+    /// ## Return value
+    ///
+    /// Returns a new keylist with the same parties but a different aggregated public key. In the
+    /// unusual case that the tweak is exactly equal to the negation of the aggregated secret key
+    /// it returns `None`.
+    pub fn tweak(&self, tweak: Scalar<impl Secrecy, impl ZeroChoice>) -> Option<Self> {
+        let mut tweak = s!(self.tweak + tweak).mark::<Public>();
+        let (agg_key, needs_negation) = g!(self.agg_key + tweak * G)
+            .mark::<NonZero>()?
+            .into_point_with_even_y();
+
+        tweak.conditional_negate(needs_negation);
+
+        let needs_negation = self.needs_negation ^ needs_negation;
+
+        Some(KeyList {
+            parties: self.parties.clone(),
+            coefs: self.coefs.clone(),
+            agg_key,
+            tweak,
+            needs_negation,
+        })
+    }
+}
+
+impl<H: Digest<OutputSize = U32> + Clone, S> MuSig<H, S> {
+    /// Generates a new key list from a list of parties.
+    ///
+    /// Each party can be local (you know the secret key) or remote (you only know the public key).
+    ///
+    /// ## Example
+    ///
+    /// ```
+    /// use schnorr_fun::{
+    ///     fun::{Point, Scalar, XOnly},
+    ///     musig::{MuSig, Party},
+    ///     nonce::Deterministic,
+    ///     Schnorr,
+    /// };
+    /// # let my_secret_key = Scalar::random(&mut rand::thread_rng());
+    /// # let their_public_key = XOnly::random(&mut rand::thread_rng());
+    /// use sha2::Sha256;
+    /// let musig = MuSig::<Sha256, Schnorr<Sha256, Deterministic<Sha256>>>::default();
+    /// let my_keypair = musig.schnorr.new_keypair(my_secret_key);
+    /// // Note the keys have to come in the same order on the other side!
+    /// let keylist = musig.new_keylist(vec![
+    ///     Party::Local(my_keypair),
+    ///     Party::Remote(their_public_key),
+    /// ]);
+    /// ```
+    pub fn new_keylist(&self, parties: Vec<Party>) -> KeyList {
+        let keys = parties
+            .iter()
+            .map(|party| match party {
+                Party::Local(keypair) => keypair.public_key(),
+                Party::Remote(xonly) => *xonly,
+            })
+            .collect::<Vec<_>>();
+
+        let coeff_hash = {
+            let L = self.pk_hash.clone().add(&keys[..]).finalize();
+            self.coeff_hash.clone().add(L.as_slice())
+        };
+
+        let mut second = None;
+        let coefs = keys
+            .iter()
+            .map(|key| {
+                // This is the logic for IsSecond from appendix B of the MuSig2 paper
+                if second.is_none() && key != &keys[0] {
+                    second = Some(key);
+                }
+                if second != Some(key) {
+                    Scalar::from_hash(coeff_hash.clone().add(key))
+                } else {
+                    Scalar::one()
+                }
+                .mark::<Public>()
+            })
+            .collect::<Vec<_>>();
+        let points = keys.into_iter().map(|x| x.to_point()).collect::<Vec<_>>();
+
+        let (agg_key, needs_negation) = crate::fun::op::lincomb(coefs.iter(), points.iter())
+            .expect_nonzero("computationally unreachable: linear combination of hash randomised points cannot add to zero")
+            .into_point_with_even_y();
+
+        KeyList {
+            parties,
+            coefs,
+            agg_key,
+            tweak: Scalar::zero().mark::<Public>(),
+            needs_negation,
+        }
+    }
+}
+
+/// A nonce (pair of points) that each party must share with the others in the first stage of signing.
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub struct Nonce(pub [Point; 2]);
+
+impl Nonce {
+    /// Reads the pair of nonces from 66 bytes (two 33-byte serialized points).
+    pub fn from_bytes(bytes: [u8; 66]) -> Option<Self> {
+        let R1 = Point::from_slice(&bytes[..33])?;
+        let R2 = Point::from_slice(&bytes[33..])?;
+        Some(Nonce([R1, R2]))
+    }
+
+    /// Serializes a public nonce as  as 66 bytes (two 33-byte serialized points).
+    pub fn to_bytes(&self) -> [u8; 66] {
+        let mut bytes = [0u8; 66];
+        bytes[..33].copy_from_slice(self.0[0].to_bytes().as_ref());
+        bytes[33..].copy_from_slice(self.0[1].to_bytes().as_ref());
+        bytes
+    }
+}
+
+secp256kfun::impl_fromstr_deserialize! {
+    name => "MuSig2 public nonce pair",
+    fn from_bytes(bytes: [u8;66]) -> Option<Nonce> {
+        Nonce::from_bytes(bytes)
+    }
+}
+
+secp256kfun::impl_display_serialize! {
+    fn to_bytes(nonce: &Nonce) -> [u8;66] {
+        nonce.to_bytes()
+    }
+}
+
+/// A pair of secret nonces along with the public portion.
+///
+/// A nonce key pair can be created manually with [`from_secrets`] or with [`MuSig::gen_nonces`].
+///
+/// [`from_secrets`]: Self::from_secrets
+/// [`MuSig::gen_nonces`]: Self::gen_nonces
+#[derive(Debug, Clone, PartialEq)]
+pub struct NonceKeyPair {
+    /// The public nonce
+    public: Nonce,
+    /// The secret nonce
+    secret: [Scalar; 2],
+}
+
+impl NonceKeyPair {
+    /// Creates a keypair from two secret scalars.
+    ///
+    /// ## Security
+    ///
+    /// You must never use the same `NonceKeyPair` into two signing sessions.
+    ///
+    /// ## Example
+    /// ```
+    /// use schnorr_fun::{fun::Scalar, musig::NonceKeyPair};
+    /// let nkp = NonceKeyPair::from_secrets([
+    ///     Scalar::random(&mut rand::thread_rng()),
+    ///     Scalar::random(&mut rand::thread_rng()),
+    /// ]);
+    /// ```
+    pub fn from_secrets(secret: [Scalar; 2]) -> Self {
+        let [ref r1, ref r2] = secret;
+        let R1 = g!(r1 * G).normalize();
+        let R2 = g!(r2 * G).normalize();
+        NonceKeyPair {
+            public: Nonce([R1, R2]),
+            secret,
+        }
+    }
+    /// Deserializes a nonce key pair from 64-bytes (two 32-byte serialized scalars).
+    pub fn from_bytes(bytes: [u8; 64]) -> Option<Self> {
+        let r1 = Scalar::from_slice(&bytes[..32])?.mark::<NonZero>()?;
+        let r2 = Scalar::from_slice(&bytes[32..])?.mark::<NonZero>()?;
+        let R1 = g!(r1 * G).normalize();
+        let R2 = g!(r2 * G).normalize();
+        let pub_nonce = Nonce([R1, R2]);
+        Some(NonceKeyPair {
+            public: pub_nonce,
+            secret: [r1, r2],
+        })
+    }
+
+    /// Serializes a nonce key pair to 64-bytes (two 32-bytes serialized scalars).
+    pub fn to_bytes(&self) -> [u8; 64] {
+        let mut bytes = [0u8; 64];
+        bytes[..32].copy_from_slice(self.secret[0].to_bytes().as_ref());
+        bytes[32..].copy_from_slice(self.secret[1].to_bytes().as_ref());
+        bytes
+    }
+
+    /// Get the secret portion of the nonce key pair (don't share this!)
+    pub fn secret(&self) -> &[Scalar; 2] {
+        &self.secret
+    }
+
+    /// Get the public portion of the nonce key pair (share this!)
+    pub fn public(&self) -> Nonce {
+        self.public
+    }
+}
+
+secp256kfun::impl_fromstr_deserialize! {
+    name => "MuSig secret nonce pair",
+    fn from_bytes(bytes: [u8;64]) -> Option<NonceKeyPair> {
+        NonceKeyPair::from_bytes(bytes)
+    }
+}
+
+secp256kfun::impl_display_serialize! {
+    fn to_bytes(nkp: &NonceKeyPair) -> [u8;64] {
+        nkp.to_bytes()
+    }
+}
+
+impl<H: Digest<OutputSize = U32> + Clone, NG: NonceGen> MuSig<H, Schnorr<H, NG>> {
+    /// Generate nonces for your local keys in keylist.
+    ///
+    /// It is very important to carefully consider the implications of your choice of underlying
+    /// [`NonceGen`].
+    ///
+    /// Using a [`Synthetic`] nonce generator will mean you don't have to worry about passing a
+    /// unique `sid` (session id) to this function for each signing session. The downside is that
+    /// you must recall the result of `gen_nonces` somewhere and store it for use when you want to
+    /// start the signing session with [`start_sign_session`].
+    ///
+    /// Using a [`Deterministic`] nonce generator means you **must** never start two signing
+    /// sessions with nonces generated from the same `sid`. If you do your secret key will be
+    /// recoverable from the two partial signatures you created with the same nonce. The upside is
+    /// that you can call [`start_sign_session_deterministic`] with the `sid` you originally passed
+    /// to `gen_nonces` without having to store the output of `gen_nonces`.
+    ///
+    /// Note that the API allows you to BYO nonces by creating `NonceKeyPair`s manually.
+    ///
+    /// [`NonceGen`]: secp256kfun::nonce::NonceGen
+    /// [`Synthetic`]: secp256kfun::nonce::Synthetic
+    /// [`Deterministic`]: secp256kfun::nonce::Deterministic
+    /// [`start_sign_session`]: Self::start_sign_session
+    /// [`start_sign_session_deterministic`]: Self::start_sign_session_deterministic
+    pub fn gen_nonces(&self, keylist: &KeyList, sid: &[u8]) -> Vec<NonceKeyPair> {
+        keylist
+            .parties
+            .iter()
+            .filter_map(|party| match party {
+                Party::Local(keypair) => {
+                    let r1 = derive_nonce!(
+                        nonce_gen => self.schnorr.nonce_gen(),
+                        secret => keypair.secret_key(),
+                        public => [ b"r1", keypair.public_key(), keylist.agg_public_key(), sid]
+                    );
+                    let r2 = derive_nonce!(
+                        nonce_gen => self.schnorr.nonce_gen(),
+                        secret => keypair.secret_key(),
+                        public => [ b"r2", keypair.public_key(), keylist.agg_public_key(), sid]
+                    );
+                    let R1 = g!(r1 * G).normalize();
+                    let R2 = g!(r2 * G).normalize();
+                    Some(NonceKeyPair {
+                        public: Nonce([R1, R2]),
+                        secret: [r1, r2],
+                    })
+                }
+                Party::Remote(_) => None,
+            })
+            .collect()
+    }
+}
+
+/// Marker type for indicating the [`SignSession`] is being used to create an ordinary Schnorr
+/// signature.
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(
+    feature = "serde",
+    derive(serde::Deserialize, serde::Serialize),
+    serde(crate = "serde_crate")
+)]
+pub struct Ordinary;
+
+/// Marks the [`SignSession`] as being used to create an adaptor (a.k.a. one-time encrypted)
+/// signature.
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(
+    feature = "serde",
+    derive(serde::Deserialize, serde::Serialize),
+    serde(crate = "serde_crate")
+)]
+pub struct Adaptor {
+    y_needs_negation: bool,
+}
+
+/// A signing session.
+///
+/// Created by [`start_sign_session`] or [`start_encrypted_sign_session`].
+/// The type parameter records whether you are trying to jointly generate a signature or an adaptor signature.
+///
+/// ## Security
+///
+/// This struct has **secret nonces** in it up until you call [`clear_secrets`] or [`sign_all`]. If
+/// a malicious party gains access to it before and you generate a partial signature with this session they
+/// will be able to recover your secret key. If this is a concern simply avoid serializing this
+/// struct (until you've cleared it) and recreate it only when you need it.
+///
+/// [`start_sign_session`]: MuSig::start_sign_session
+/// [`start_encrypted_sign_session`]: MuSig::start_encrypted_sign_session
+/// [`clear_secrets`]: SignSession::clear_secrets
+/// [`sign_all`]: MuSig::sign_all
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(
+    feature = "serde",
+    derive(serde::Deserialize, serde::Serialize),
+    serde(crate = "serde_crate")
+)]
+pub struct SignSession<T = Ordinary> {
+    b: Scalar<Public, Zero>,
+    c: Scalar<Public, Zero>,
+    local_secret_nonces: Vec<[Scalar; 2]>,
+    public_nonces: Vec<Nonce>,
+    R: Point<EvenY>,
+    signing_type: T,
+}
+
+impl<T> SignSession<T> {
+    /// Removes all secret nonce data from the session.
+    ///
+    /// The session works as normal except that it can no longer be used for signing ([`sign_all`] will return an empty vector).
+    ///
+    /// [`sign_all`]: MuSig::sign_all
+    pub fn clear_secrets(&mut self) {
+        self.local_secret_nonces.drain(..);
+    }
+}
+
+impl<H: Digest<OutputSize = U32> + Clone, NG> MuSig<H, Schnorr<H, NG>> {
+    /// Start a signing session.
+    ///
+    /// You must provide you local secret nonces (the public portion must be shared with the other signer(s)).
+    /// If you are using deterministic nonce generations it's possible to use [`start_sign_session_deterministic`] instead.
+    ///
+    /// ## Return Value
+    ///
+    /// Returns `None` in the case that the `remote_nonces` have been (maliciously) selected to
+    /// cancel out your local nonces.
+    /// This is not a security issue -- we just can't continue the protocol if this happens.
+    ///
+    /// # Panics
+    ///
+    /// Panics if number of local or remote nonces passed in does not align with the parties in
+    /// `keylist`.
+    ///
+    /// [`start_sign_session_deterministic`]: Self::start_sign_session_deterministic
+    pub fn start_sign_session(
+        &self,
+        keylist: &KeyList,
+        local_nonces: Vec<NonceKeyPair>,
+        remote_nonces: impl IntoIterator<Item = Nonce>,
+        message: Message<'_, Public>,
+    ) -> Option<SignSession> {
+        let (b, c, local_secret_nonces, public_nonces, R, _) = self._start_sign_session(
+            keylist,
+            local_nonces,
+            remote_nonces,
+            message,
+            &Point::zero(),
+        )?;
+        Some(SignSession {
+            b,
+            c,
+            local_secret_nonces,
+            public_nonces,
+            R,
+            signing_type: Ordinary,
+        })
+    }
+
+    /// Start an encrypted signing session.
+    ///
+    /// i.e. a session to produce an adaptor signature under `encryption_key`.
+    /// See [`adaptor`] for a more general description of adaptor signatures.
+    ///
+    /// You must provide you local secret nonces (the public portion must be shared with the other
+    /// signer(s)). If you are using deterministic nonce generation it's possible to use
+    /// [`start_encrypted_sign_session_deterministic`] instead.
+    ///
+    /// ## Return Value
+    ///
+    /// Returns `None` in the case that the `remote_nonces` have been (maliciously) selected to
+    /// cancel out your local nonces.
+    /// This is not a security issue -- we just can't continue the protocol if this happens.
+    ///
+    /// # Panics
+    ///
+    /// Panics if number of local or remote nonces passed in does not align with the parties in
+    /// `keylist`.
+    ///
+    /// [`start_encrypted_sign_session_deterministic`]: Self::start_sign_session_deterministic
+    /// [`adaptor`]: crate::adaptor
+    pub fn start_encrypted_sign_session(
+        &self,
+        keylist: &KeyList,
+        local_nonces: Vec<NonceKeyPair>,
+        remote_nonces: impl IntoIterator<Item = Nonce>,
+        message: Message<'_, Public>,
+        encryption_key: &Point<impl PointType, impl Secrecy>,
+    ) -> Option<SignSession<Adaptor>> {
+        let (b, c, local_secret_nonces, public_nonces, R, y_needs_negation) = self
+            ._start_sign_session(
+                keylist,
+                local_nonces,
+                remote_nonces,
+                message,
+                encryption_key,
+            )?;
+        Some(SignSession {
+            b,
+            c,
+            local_secret_nonces,
+            public_nonces,
+            R,
+            signing_type: Adaptor { y_needs_negation },
+        })
+    }
+
+    fn _start_sign_session(
+        &self,
+        keylist: &KeyList,
+        local_nonces: Vec<NonceKeyPair>,
+        remote_nonces: impl IntoIterator<Item = Nonce>,
+        message: Message<'_, Public>,
+        encryption_key: &Point<impl PointType, impl Secrecy, impl ZeroChoice>,
+    ) -> Option<(
+        Scalar<Public, Zero>,
+        Scalar<Public, Zero>,
+        Vec<[Scalar; 2]>,
+        Vec<Nonce>,
+        Point<EvenY>,
+        bool,
+    )> {
+        let mut remote_nonces = remote_nonces.into_iter();
+        let mut local_pubnonces = local_nonces.iter().map(|nonce| nonce.public);
+
+        let mut Rs = keylist
+            .parties
+            .iter()
+            .map(|party| match party {
+                Party::Local(_) => local_pubnonces
+                    .next()
+                    .expect("missing local nonce keypair -- must have one for each local party"),
+                Party::Remote(_) => remote_nonces.next().expect("missing remote nonce"),
+            })
+            .collect::<Vec<_>>();
+
+        assert!(
+            remote_nonces.next().is_none(),
+            "Too many remote nonces passed in"
+        );
+        assert!(
+            local_pubnonces.next().is_none(),
+            "Too many local nonces passed in"
+        );
+
+        let agg_Rs = Rs
+            .iter()
+            .fold([Point::zero().mark::<Jacobian>(); 2], |acc, nonce| {
+                [
+                    g!({ acc[0] } + { nonce.0[0] }),
+                    g!({ acc[1] } + { nonce.0[1] }),
+                ]
+            });
+        let agg_Rs = [
+            g!({ agg_Rs[0] } + encryption_key)
+                .normalize()
+                .mark::<NonZero>()?,
+            agg_Rs[1].normalize().mark::<NonZero>()?,
+        ];
+
+        let b = {
+            let H = self.nonce_coeff_hash.clone();
+            Scalar::from_hash(H.add(agg_Rs).add(keylist.agg_public_key()).add(message))
+        }
+        .mark::<(Public, Zero)>();
+
+        let (R, r_needs_negation) = g!({ agg_Rs[0] } + b * { agg_Rs[1] } )
+            .normalize()
+            .expect_nonzero("computationally unreachable: one of the coefficients is a hash output that commits to both point")
+            .into_point_with_even_y();
+
+        for R in &mut Rs {
+            R.0[0] = R.0[0].conditional_negate(r_needs_negation);
+            R.0[1] = R.0[1].conditional_negate(r_needs_negation);
+        }
+
+        let local_secret_nonces = local_nonces
+            .into_iter()
+            .map(|local_nonce| {
+                let mut secret = local_nonce.secret;
+                secret[0].conditional_negate(r_needs_negation);
+                secret[1].conditional_negate(r_needs_negation);
+                secret
+            })
+            .collect();
+
+        let c = self
+            .schnorr
+            .challenge(R.to_xonly(), keylist.agg_public_key(), message);
+
+        Some((b, c, local_secret_nonces, Rs, R, r_needs_negation))
+    }
+
+    /// Generates partial signatures (or partial encrypted signatures depending on `T`) under each of the `Local` entries in `keylist`.
+    ///
+    /// The order of the partial signatures returned is the order of them in the keylist.
+    ///
+    /// This can only be called once per session as it clears the session (see also [`clear_secrets`]).
+    /// Calling `sign_all` again will return an empty vector.
+    ///
+    /// [`clear_secrets`]: SignSession::clear_secrets
+    pub fn sign_all<T>(
+        &self,
+        keylist: &KeyList,
+        session: &mut SignSession<T>,
+    ) -> Vec<Scalar<Public, Zero>> {
+        let c = session.c;
+        let b = session.b;
+
+        keylist
+            .parties
+            .iter()
+            .enumerate()
+            .filter_map(|(i, party)| match party {
+                Party::Local(keypair) => Some((i, keypair)),
+                Party::Remote(_) => None,
+            })
+            .zip(session.local_secret_nonces.drain(..))
+            .map(|((i, keypair), secret_nonces)| {
+                let x = keypair.secret_key();
+                let [r1, r2] = secret_nonces;
+                let mut a = keylist.coefs[i];
+                a.conditional_negate(keylist.needs_negation);
+                s!(c * a * x + r1 + b * r2).mark::<(Public, Zero)>()
+            })
+            .collect()
+    }
+
+    #[must_use]
+    /// Verifies a partial signature (or partial encrypted signature depending on `T`).
+    ///
+    /// You must provide the `index` of the party (the index of the key in `keylist`).
+    ///
+    /// # Panics
+    ///
+    /// Panics when `index` is equal to or greater than the number of parties in the keylist.
+    pub fn verify_partial_signature<T>(
+        &self,
+        keylist: &KeyList,
+        session: &SignSession<T>,
+        index: usize,
+        partial_sig: Scalar<Public, Zero>,
+    ) -> bool {
+        let c = session.c;
+        let b = session.b;
+        let s = &partial_sig;
+        let mut a = keylist.coefs[index].clone();
+        a.conditional_negate(keylist.needs_negation);
+        let X = keylist.keys().nth(index).unwrap().to_point();
+        let [ref R1, ref R2] = &session.public_nonces[index].0;
+        g!((c * a) * X + R1 + b * R2 - s * G).is_zero()
+    }
+
+    /// Combines all the partial signatures into a single `Signature`.
+    ///
+    /// Note this does not check the validity of any of the partial signatures. You should either check
+    /// each one using [`verify_partial_signature`] or use [`verify`] on the returned `Signature` to check validity.
+    ///
+    /// [`verify`]: crate::Schnorr::verify
+    /// [`verify_partial_signature`]: Self::verify_partial_signature
+    pub fn combine_partial_signatures(
+        &self,
+        keylist: &KeyList,
+        session: &SignSession<Ordinary>,
+        partial_sigs: impl IntoIterator<Item = Scalar<Public, Zero>>,
+    ) -> Signature {
+        let (R, s) = self._combine_partial_signatures(keylist, &session, partial_sigs);
+        Signature { R: R.to_xonly(), s }
+    }
+
+    /// Combines all the partial encrypted signatures into one encrypted signature.
+    ///
+    /// Note this does not check the validity of any of the partial signatures. You should either check
+    /// each one using [`verify_partial_signature`] or use [`verify_encrypted_signature`] on the returned `Signature` to check validity.
+    ///
+    /// [`verify_encrypted_signature`]: crate::adaptor::Adaptor::verify_encrypted_signature
+    /// [`verify_partial_signature`]: Self::verify_partial_signature
+    pub fn combine_partial_encrypted_signatures(
+        &self,
+        keylist: &KeyList,
+        session: &SignSession<Adaptor>,
+        partial_encrypted_sigs: impl IntoIterator<Item = Scalar<Public, Zero>>,
+    ) -> EncryptedSignature {
+        let (R, s_hat) =
+            self._combine_partial_signatures(keylist, &session, partial_encrypted_sigs);
+        EncryptedSignature {
+            R,
+            s_hat,
+            needs_negation: session.signing_type.y_needs_negation,
+        }
+    }
+
+    fn _combine_partial_signatures<T>(
+        &self,
+        keylist: &KeyList,
+        session: &SignSession<T>,
+        partial_sigs: impl IntoIterator<Item = Scalar<Public, Zero>>,
+    ) -> (Point<EvenY>, Scalar<Public, Zero>) {
+        let ck = s!(session.c * keylist.tweak);
+        let sum_s = partial_sigs
+            .into_iter()
+            .reduce(|acc, s| s!(acc + s).mark::<Public>())
+            .unwrap_or(Scalar::zero().mark::<Public>());
+        let s = s!(sum_s + ck).mark::<Public>();
+        (session.R, s)
+    }
+}
+
+impl<H: Digest<OutputSize = U32> + Clone, HNG> MuSig<H, Schnorr<H, Deterministic<HNG>>>
+where
+    Deterministic<HNG>: NonceGen,
+{
+    /// Same as [`start_sign_session`] but re-generate the local nonces deterministically from the
+    /// `sid` instead of passing them in.
+    ///
+    /// ## Security
+    ///
+    /// Each call to this function must have a unique `sid`. Never call it twice with the same
+    /// `sid`, otherwise you risk revealing your secret key with the two signatures generated from
+    /// it if `message` or `remote_nonces` changes.
+    ///
+    /// [`start_sign_session`]: Self::start_sign_session
+    pub fn start_sign_session_deterministic(
+        &self,
+        keylist: &KeyList,
+        remote_nonces: impl IntoIterator<Item = Nonce>,
+        sid: &[u8],
+        message: Message<'_, Public>,
+    ) -> Option<SignSession> {
+        let local_nonces = self.gen_nonces(keylist, sid);
+        self.start_sign_session(keylist, local_nonces, remote_nonces, message)
+    }
+
+    /// Same as [`start_encrypted_sign_session`] but re-generate the local nonces deterministically from the
+    /// `sid` instead of passing them in.
+    ///
+    /// ## Security
+    ///
+    /// Each call to this function must have a unique `sid`. Never call it twice with the same
+    /// `sid`, otherwise you risk revealing your secret key with the two signatures generated from
+    /// it if `message` or `remote_nonces` changes.
+    ///
+    /// [`start_encrypted_sign_session`]: Self::start_encrypted_sign_session
+    pub fn start_encrypted_sign_session_deterministic(
+        &self,
+        keylist: &KeyList,
+        remote_nonces: impl IntoIterator<Item = Nonce>,
+        sid: &[u8],
+        message: Message<'_, Public>,
+        encryption_key: &Point<impl PointType, impl Secrecy>,
+    ) -> Option<SignSession<Adaptor>> {
+        let local_nonces = self.gen_nonces(keylist, sid);
+        self.start_encrypted_sign_session(
+            keylist,
+            local_nonces,
+            remote_nonces,
+            message,
+            encryption_key,
+        )
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::adaptor::Adaptor;
+
+    use super::*;
+    use secp256kfun::{nonce::Deterministic, proptest::prelude::*};
+    use sha2::Sha256;
+
+    proptest! {
+        #[test]
+        fn test_end_to_end(sk1 in any::<Scalar>(), sk2 in any::<Scalar>(), sk3 in any::<Scalar>(), tweak in any::<Scalar<Public, Zero>>(), use_tweak in any::<bool>()) {
+            let schnorr = Schnorr::<Sha256, _>::new(Deterministic::<Sha256>::default());
+            let musig = MuSig::new(schnorr);
+            let keypair1 = musig
+                .schnorr
+                .new_keypair(sk1);
+            let keypair2 = musig
+                .schnorr
+                .new_keypair(sk2);
+            let keypair3 = musig
+                .schnorr
+                .new_keypair(sk3);
+            let mut keylist_p1 = musig.new_keylist(vec![
+                Party::Local(keypair1.clone()),
+                Party::Remote(keypair2.public_key()),
+                Party::Local(keypair3.clone()),
+            ]);
+            let mut keylist_p2 = musig.new_keylist(vec![
+                Party::Remote(keypair1.public_key()),
+                Party::Local(keypair2),
+                Party::Remote(keypair3.public_key()),
+            ]);
+
+            if use_tweak {
+                keylist_p1 = keylist_p1.tweak(tweak).unwrap();
+                keylist_p2 = keylist_p2.tweak(tweak).unwrap();
+            }
+            assert_eq!(keylist_p1.agg_public_key(), keylist_p2.agg_public_key());
+
+            let p1_nonces = musig.gen_nonces(&keylist_p1, b"test");
+            let p2_nonces = musig.gen_nonces(&keylist_p2, b"test");
+            let message =
+                Message::<Public>::plain("test", b"Chancellor on brink of second bailout for banks");
+
+            let mut p1_session = musig
+                .start_sign_session(
+                    &keylist_p1,
+                    p1_nonces.clone(),
+                    p2_nonces.iter().map(|nonce| nonce.public),
+                    message,
+                )
+                .unwrap();
+            let mut p2_session = musig
+                .start_sign_session_deterministic(
+                    &keylist_p2,
+                    p1_nonces.iter().map(|nonce| nonce.public),
+                    b"test",
+                    message,
+                )
+                .unwrap();
+            let p1_sigs = musig.sign_all(&keylist_p1, &mut p1_session);
+            assert_eq!(musig.sign_all(&keylist_p1, &mut p1_session).len(), 0, "sign_all should hose the session");
+
+            assert_eq!(p1_sigs.len(), 2);
+            for (j, i) in [0, 2].iter().enumerate() {
+                assert!(musig.verify_partial_signature(&keylist_p2, &p2_session, *i, p1_sigs[j]));
+                assert!(musig.verify_partial_signature(&keylist_p1, &p1_session, *i, p1_sigs[j]));
+            }
+
+            let p2_sigs = musig.sign_all(&keylist_p2, &mut p2_session);
+            assert_eq!(p2_sigs.len(), 1);
+            assert!(musig.verify_partial_signature(&keylist_p2, &p2_session, 1, p2_sigs[0]));
+            assert!(musig.verify_partial_signature(&keylist_p1, &p1_session, 1, p2_sigs[0]));
+
+            let partial_sigs = [p1_sigs, p2_sigs].concat();
+            let sig_p1 = musig.combine_partial_signatures(&keylist_p1, &p1_session, partial_sigs.clone());
+            let sig_p2 = musig.combine_partial_signatures(&keylist_p2, &p2_session, partial_sigs);
+            assert_eq!(sig_p1, sig_p2);
+            assert!(musig
+                    .schnorr
+                    .verify(&keylist_p1.agg_verification_key(), message, &sig_p1));
+            assert!(musig
+                    .schnorr
+                    .verify(&keylist_p2.agg_verification_key(), message, &sig_p2));
+        }
+
+        #[test]
+        fn test_musig_adaptor(sk1 in any::<Scalar>(), sk2 in any::<Scalar>(), sk3 in any::<Scalar>(), y in any::<Scalar>()) {
+            let schnorr = Schnorr::<Sha256, _>::new(Deterministic::<Sha256>::default());
+            let musig = MuSig::new(schnorr);
+            let keypair1 = musig
+                .schnorr
+                .new_keypair(sk1);
+            let keypair2 = musig
+                .schnorr
+                .new_keypair(sk2);
+            let keypair3 = musig
+                .schnorr
+                .new_keypair(sk3);
+            let encryption_key = musig.schnorr.encryption_key_for(&y);
+            let keylist_p1 = musig.new_keylist(vec![
+                Party::Local(keypair1.clone()),
+                Party::Remote(keypair2.public_key()),
+                Party::Local(keypair3.clone()),
+            ]);
+            let keylist_p2 = musig.new_keylist(vec![
+                Party::Remote(keypair1.public_key()),
+                Party::Local(keypair2),
+                Party::Remote(keypair3.public_key()),
+            ]);
+            assert_eq!(keylist_p1.agg_public_key(), keylist_p2.agg_public_key());
+
+            let p1_nonces = musig.gen_nonces(&keylist_p1, b"test");
+            let p2_nonces = musig.gen_nonces(&keylist_p2, b"test");
+            let message =
+                Message::<Public>::plain("test", b"Chancellor on brink of second bailout for banks");
+
+            let mut p1_session = musig
+                .start_encrypted_sign_session(
+                    &keylist_p1,
+                    p1_nonces.clone(),
+                    p2_nonces.iter().map(|nonce| nonce.public),
+                    message,
+                    &encryption_key
+                )
+                .unwrap();
+            let mut p2_session = musig
+                .start_encrypted_sign_session_deterministic(
+                    &keylist_p2,
+                    p1_nonces.iter().map(|nonce| nonce.public),
+                    b"test",
+                    message,
+                    &encryption_key
+                )
+                .unwrap();
+            let p1_sigs = musig.sign_all(&keylist_p1, &mut p1_session);
+
+            assert_eq!(p1_sigs.len(), 2);
+            for (j, i) in [0, 2].iter().enumerate() {
+                assert!(musig.verify_partial_signature(&keylist_p2, &p2_session, *i, p1_sigs[j]));
+                assert!(musig.verify_partial_signature(&keylist_p1, &p1_session, *i, p1_sigs[j]));
+            }
+
+            let p2_sigs = musig.sign_all(&keylist_p2, &mut p2_session);
+            assert_eq!(p2_sigs.len(), 1);
+            assert!(musig.verify_partial_signature(&keylist_p2, &p2_session, 1, p2_sigs[0]));
+            assert!(musig.verify_partial_signature(&keylist_p1, &p1_session, 1, p2_sigs[0]));
+
+            let partial_sigs = [p1_sigs, p2_sigs].concat();
+            let sig_p1 = musig.combine_partial_encrypted_signatures(&keylist_p1, &p1_session, partial_sigs.clone());
+            let sig_p2 = musig.combine_partial_encrypted_signatures(&keylist_p2, &p2_session, partial_sigs);
+            assert_eq!(sig_p1, sig_p2);
+            assert!(musig
+                    .schnorr
+                    .verify_encrypted_signature(&keylist_p1.agg_verification_key(), &encryption_key, message, &sig_p1));
+            assert!(musig
+                    .schnorr
+                    .verify_encrypted_signature(&keylist_p2.agg_verification_key(), &encryption_key, message, &sig_p2));
+
+        }
+
+    }
+    #[test]
+    fn sign_test_vectors() {
+        let musig = MuSig::<Sha256, Schnorr<Sha256, Deterministic<Sha256>>>::default();
+
+        let X1 = XOnly::from_bytes([
+            0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D,
+            0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13,
+            0xBC, 0xE0, 0x36, 0xF9,
+        ])
+        .unwrap();
+        let X2 = XOnly::from_bytes([
+            0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23,
+            0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B,
+            0x50, 0x2B, 0xA6, 0x59,
+        ])
+        .unwrap();
+        let keypair = musig.schnorr.new_keypair(
+            Scalar::from_bytes([
+                0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E,
+                0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2,
+                0xD1, 0x00, 0x76, 0x71,
+            ])
+            .unwrap()
+            .mark::<NonZero>()
+            .unwrap(),
+        );
+
+        let secnonce = NonceKeyPair::from_bytes([
+            0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59,
+            0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8,
+            0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81,
+            0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5,
+            0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7,
+        ])
+        .unwrap();
+
+        let agg_pubnonce = Nonce::from_bytes([
+            0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33,
+            0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42,
+            0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45,
+            0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10,
+            0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9,
+        ])
+        .unwrap();
+        let remote_nonces = vec![
+            agg_pubnonce,
+            Nonce([-secnonce.public.0[0], -secnonce.public.0[1]]),
+        ];
+        let msg = [
+            0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26,
+            0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD,
+            0xDD, 0xF3, 0xC0, 0xCF,
+        ];
+        {
+            let keylist = musig.new_keylist(vec![
+                Party::Local(keypair.clone()),
+                Party::Remote(X1),
+                Party::Remote(X2),
+            ]);
+            let mut session = musig
+                .start_sign_session(
+                    &keylist,
+                    vec![secnonce.clone()],
+                    remote_nonces.clone(),
+                    Message::<Public>::raw(&msg),
+                )
+                .unwrap();
+
+            let scalar = musig.sign_all(&keylist, &mut session)[0];
+            let expected = Scalar::from_bytes([
+                0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E,
+                0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12,
+                0x40, 0x3B, 0x4D, 0x3B,
+            ])
+            .unwrap();
+            assert_eq!(scalar, expected)
+        }
+
+        {
+            let keylist = musig.new_keylist(vec![
+                Party::Remote(X1),
+                Party::Local(keypair.clone()),
+                Party::Remote(X2),
+            ]);
+            let mut session = musig
+                .start_sign_session(
+                    &keylist,
+                    vec![secnonce.clone()],
+                    remote_nonces.clone(),
+                    Message::<Public>::raw(&msg),
+                )
+                .unwrap();
+
+            let scalar = musig.sign_all(&keylist, &mut session)[0];
+            let expected = Scalar::from_bytes([
+                0x2D, 0xF6, 0x7B, 0xFF, 0xF1, 0x8E, 0x3D, 0xE7, 0x97, 0xE1, 0x3C, 0x64, 0x75, 0xC9,
+                0x63, 0x04, 0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35, 0x7C, 0xEC, 0xA7, 0xC8,
+                0x42, 0x42, 0x95, 0xEA,
+            ])
+            .unwrap();
+            assert_eq!(scalar, expected)
+        }
+
+        {
+            let keylist = musig.new_keylist(vec![
+                Party::Remote(X1),
+                Party::Remote(X2),
+                Party::Local(keypair.clone()),
+            ]);
+            let mut session = musig
+                .start_sign_session(
+                    &keylist,
+                    vec![secnonce.clone()],
+                    remote_nonces.clone(),
+                    Message::<Public>::raw(&msg),
+                )
+                .unwrap();
+
+            let scalar = musig.sign_all(&keylist, &mut session)[0];
+            let expected = Scalar::from_bytes([
+                0x0D, 0x5B, 0x65, 0x1E, 0x6D, 0xE3, 0x4A, 0x29, 0xA1, 0x2D, 0xE7, 0xA8, 0xB4, 0x18,
+                0x3B, 0x4A, 0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF, 0xA4, 0xA3, 0xD1, 0xBC,
+                0xAA, 0xBC, 0x75, 0x17,
+            ])
+            .unwrap();
+            assert_eq!(scalar, expected)
+        }
+
+        {
+            let tweak = Scalar::from_bytes([
+                0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A,
+                0x72, 0x3D, 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C,
+                0xDF, 0xCD, 0x83, 0xBB,
+            ])
+            .unwrap();
+            let keylist = musig
+                .new_keylist(vec![
+                    Party::Remote(X1),
+                    Party::Remote(X2),
+                    Party::Local(keypair.clone()),
+                ])
+                .tweak(tweak)
+                .unwrap();
+            let mut session = musig
+                .start_sign_session(
+                    &keylist,
+                    vec![secnonce.clone()],
+                    remote_nonces.clone(),
+                    Message::<Public>::raw(&msg),
+                )
+                .unwrap();
+
+            let scalar = musig.sign_all(&keylist, &mut session)[0];
+            let expected = Scalar::from_bytes([
+                0x5E, 0x24, 0xC7, 0x49, 0x6B, 0x56, 0x5D, 0xEB, 0xC3, 0xB9, 0x63, 0x9E, 0x6F, 0x13,
+                0x04, 0xA2, 0x15, 0x97, 0xF9, 0x60, 0x3D, 0x3A, 0xB0, 0x5B, 0x49, 0x13, 0x64, 0x17,
+                0x75, 0xE1, 0x37, 0x5B,
+            ])
+            .unwrap();
+
+            assert!(musig.verify_partial_signature(&keylist, &mut session, 2, scalar));
+            assert_eq!(scalar, expected)
+        }
+
+        {
+            let sec_adaptor = Scalar::from_bytes([
+                0xD5, 0x6A, 0xD1, 0x85, 0x00, 0xF2, 0xD7, 0x8A, 0xB9, 0x54, 0x80, 0x53, 0x76, 0xF3,
+                0x9D, 0x1B, 0x6D, 0x62, 0x04, 0x95, 0x12, 0x39, 0x04, 0x6D, 0x99, 0x3A, 0x9C, 0x31,
+                0xE0, 0xF4, 0x78, 0x71,
+            ])
+            .unwrap()
+            .expect_nonzero("");
+            let pub_adatpor = g!(sec_adaptor * G);
+
+            let keylist = musig.new_keylist(vec![
+                Party::Remote(X1),
+                Party::Remote(X2),
+                Party::Local(keypair),
+            ]);
+
+            let mut session = musig
+                .start_encrypted_sign_session(
+                    &keylist,
+                    vec![secnonce.clone()],
+                    remote_nonces.clone(),
+                    Message::<Public>::raw(&msg),
+                    &pub_adatpor,
+                )
+                .unwrap();
+
+            let scalar = musig.sign_all(&keylist, &mut session)[0];
+            let expected = Scalar::from_bytes([
+                0xD7, 0x67, 0xD0, 0x7D, 0x9A, 0xB8, 0x19, 0x8C, 0x9F, 0x64, 0xE3, 0xFD, 0x9F, 0x7B,
+                0x8B, 0xAA, 0xC6, 0x05, 0xF1, 0x8D, 0xFF, 0x18, 0x95, 0x24, 0x2D, 0x93, 0x95, 0xD9,
+                0xC8, 0xE6, 0xDD, 0x7C,
+            ])
+            .unwrap();
+            assert_eq!(scalar, expected)
+        }
+    }
+
+    #[test]
+    fn test_key_aggregation() {
+        let musig = MuSig::<Sha256>::keyagg_only();
+
+        // test taken from
+        // https://github.com/ElementsProject/secp256k1-zkp/blob/5d2df0541960554be5c0ba58d86e5fa479935000/src/modules/musig/tests_impl.h
+        let x1 = XOnly::from_bytes([
+            0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D,
+            0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13,
+            0xBC, 0xE0, 0x36, 0xF9,
+        ])
+        .unwrap();
+        let x2 = XOnly::from_bytes([
+            0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23,
+            0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B,
+            0x50, 0x2B, 0xA6, 0x59,
+        ])
+        .unwrap();
+        let x3 = XOnly::from_bytes([
+            0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80,
+            0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68,
+            0xD0, 0x38, 0xCA, 0x66,
+        ])
+        .unwrap();
+
+        let x1_x2_x3 = vec![x1, x2, x3];
+        let expected_x1_x2_x3 = XOnly::from_bytes([
+            0xE5, 0x83, 0x01, 0x40, 0x51, 0x21, 0x95, 0xD7, 0x4C, 0x83, 0x07, 0xE3, 0x96, 0x37,
+            0xCB, 0xE5, 0xFB, 0x73, 0x0E, 0xBE, 0xAB, 0x80, 0xEC, 0x51, 0x4C, 0xF8, 0x8A, 0x87,
+            0x7C, 0xEE, 0xEE, 0x0B,
+        ])
+        .unwrap();
+        assert_eq!(
+            musig
+                .new_keylist(x1_x2_x3.into_iter().map(Party::Remote).collect())
+                .agg_public_key(),
+            expected_x1_x2_x3
+        );
+
+        let x3_x2_x1 = vec![x3, x2, x1];
+        let expected_x3_x2_x1 = XOnly::from_bytes([
+            0xD7, 0x0C, 0xD6, 0x9A, 0x26, 0x47, 0xF7, 0x39, 0x09, 0x73, 0xDF, 0x48, 0xCB, 0xFA,
+            0x2C, 0xCC, 0x40, 0x7B, 0x8B, 0x2D, 0x60, 0xB0, 0x8C, 0x5F, 0x16, 0x41, 0x18, 0x5C,
+            0x79, 0x98, 0xA2, 0x90,
+        ])
+        .unwrap();
+        assert_eq!(
+            musig
+                .new_keylist(x3_x2_x1.into_iter().map(Party::Remote).collect())
+                .agg_public_key(),
+            expected_x3_x2_x1
+        );
+
+        let x1_x1_x1 = vec![x1, x1, x1];
+        let expected_x1_x1_x1 = XOnly::from_bytes([
+            0x81, 0xA8, 0xB0, 0x93, 0x91, 0x2C, 0x9E, 0x48, 0x14, 0x08, 0xD0, 0x97, 0x76, 0xCE,
+            0xFB, 0x48, 0xAE, 0xB8, 0xB6, 0x54, 0x81, 0xB6, 0xBA, 0xAF, 0xB3, 0xC5, 0x81, 0x01,
+            0x06, 0x71, 0x7B, 0xEB,
+        ])
+        .unwrap();
+        assert_eq!(
+            musig
+                .new_keylist(x1_x1_x1.into_iter().map(Party::Remote).collect())
+                .agg_public_key(),
+            expected_x1_x1_x1
+        );
+
+        let x1_x1_x2_x2 = vec![x1, x1, x2, x2];
+        let expected_x1_x1_x2_x2 = XOnly::from_bytes([
+            0x2E, 0xB1, 0x88, 0x51, 0x88, 0x7E, 0x7B, 0xDC, 0x5E, 0x83, 0x0E, 0x89, 0xB1, 0x9D,
+            0xDB, 0xC2, 0x80, 0x78, 0xF1, 0xFA, 0x88, 0xAA, 0xD0, 0xAD, 0x01, 0xCA, 0x06, 0xFE,
+            0x4F, 0x80, 0x21, 0x0B,
+        ])
+        .unwrap();
+        assert_eq!(
+            musig
+                .new_keylist(x1_x1_x2_x2.into_iter().map(Party::Remote).collect())
+                .agg_public_key(),
+            expected_x1_x1_x2_x2
+        );
+    }
+}
diff --git a/schnorr_fun/src/schnorr.rs b/schnorr_fun/src/schnorr.rs
index 0f28421f..ba7a42b0 100644
--- a/schnorr_fun/src/schnorr.rs
+++ b/schnorr_fun/src/schnorr.rs
@@ -1,12 +1,12 @@
 use crate::{
     fun::{
-        self, derive_nonce,
+        derive_nonce,
         digest::{generic_array::typenum::U32, Digest},
         g,
         hash::{HashAdd, Tagged},
         marker::*,
         nonce::{AddTag, NonceGen},
-        s, Point, Scalar, XOnly,
+        s, Point, Scalar, XOnly, G,
     },
     KeyPair, Message, Signature,
 };
@@ -14,18 +14,14 @@ use crate::{
 /// An instance of a [BIP-340] style Schnorr signature scheme.
 ///
 /// Each instance is defined by its:
-/// - `G`: Public generator (usually [`G`])
 /// - `challenge_hash`: The hash function instance that is used to produce the [_Fiat-Shamir_] challenge.
 /// - `nonce_gen`: The [`NonceGen`] used to hash the signing inputs (and perhaps additional randomness) to produce the secret nonce.
 ///
 /// [_Fiat-Shamir_]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic
-/// [`G`]: crate::fun::G
 /// [`NonceGen<H>`]: crate::fun::hash::NonceGen
 /// [BIP-340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
 #[derive(Clone)]
-pub struct Schnorr<CH, NG = (), GT = BasePoint> {
-    /// The generator point the Schnorr signature scheme is defined with.
-    G: Point<GT>,
+pub struct Schnorr<CH, NG = ()> {
     /// The [`NonceGen`] used to generate nonces.
     ///
     /// [`NonceGen`]: crate::nonce::NonceGen
@@ -34,18 +30,23 @@ pub struct Schnorr<CH, NG = (), GT = BasePoint> {
     challenge_hash: CH,
 }
 
-impl<H: Digest<OutputSize = U32> + Tagged> Schnorr<H, (), BasePoint> {
+impl<H: Digest<OutputSize = U32> + Tagged> Schnorr<H, ()> {
     /// Create a new instance that can only verify signatures.
     ///
-    /// The instance will use the standard value of [`G`].
+    /// # Example
+    ///
+    /// ```
+    /// use schnorr_fun::Schnorr;
+    /// use sha2::Sha256;
     ///
-    /// [`G`]: secp256kfun::G
+    /// let schnorr = Schnorr::<Sha256>::verify_only();
+    /// ```
     pub fn verify_only() -> Self {
         Self::new(())
     }
 }
 
-impl<CH, NG> Schnorr<CH, NG, BasePoint>
+impl<CH, NG> Schnorr<CH, NG>
 where
     CH: Digest<OutputSize = U32> + Tagged,
     NG: AddTag,
@@ -71,14 +72,31 @@ where
     pub fn new(nonce_gen: NG) -> Self {
         let nonce_gen = nonce_gen.add_tag("BIP0340");
         Self {
-            G: fun::G.clone(),
             nonce_gen,
             challenge_hash: CH::default().tagged("BIP0340/challenge".as_bytes()),
         }
     }
 }
 
-impl<NG, CH, GT> Schnorr<CH, NG, GT>
+impl<CH: Default + Tagged + Digest<OutputSize = U32>, NG: Default + AddTag> Default
+    for Schnorr<CH, NG>
+{
+    /// Returns a Schnorr instance tagged in the default way according to BIP340.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use schnorr_fun::{nonce::Deterministic, Schnorr};
+    /// use sha2::Sha256;
+    ///
+    /// let schnorr = Schnorr::<Sha256, Deterministic<Sha256>>::default();
+    /// ```
+    fn default() -> Self {
+        Self::new(NG::default())
+    }
+}
+
+impl<NG, CH> Schnorr<CH, NG>
 where
     CH: Digest<OutputSize = U32> + Clone,
     NG: NonceGen,
@@ -110,8 +128,8 @@ where
             public => [X, message]
         );
 
-        let R = XOnly::from_scalar_mul(&self.G, &mut r);
-        let c = self.challenge(&R, X, message);
+        let R = XOnly::from_scalar_mul(&G, &mut r);
+        let c = self.challenge(R, X, message);
         let s = s!(r + c * x).mark::<Public>();
 
         Signature { R, s }
@@ -125,12 +143,7 @@ where
     }
 }
 
-impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
-    /// Returns the generator point being used for the scheme.
-    pub fn G(&self) -> &Point<GT> {
-        &self.G
-    }
-
+impl<NG, CH: Digest<OutputSize = U32> + Clone> Schnorr<CH, NG> {
     /// Returns the challenge hash being used to sign/verify signatures
     pub fn challenge_hash(&self) -> CH {
         self.challenge_hash.clone()
@@ -145,7 +158,7 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
     /// [`Point`]: crate::fun::Point
     /// [`EvenY`]: crate::fun::marker::EvenY
     pub fn new_keypair(&self, mut sk: Scalar) -> KeyPair {
-        let pk = XOnly::from_scalar_mul(&self.G, &mut sk);
+        let pk = XOnly::from_scalar_mul(&G, &mut sk);
         KeyPair { sk, pk }
     }
 
@@ -160,15 +173,15 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
     ///
     /// ```
     /// use schnorr_fun::{
-    ///     fun::{marker::*, s, Scalar, XOnly},
+    ///     fun::{marker::*, s, Scalar, XOnly, G},
     ///     Message, Schnorr, Signature,
     /// };
     /// # let schnorr = schnorr_fun::test_instance!();
     /// let message = Message::<Public>::plain("my-app", b"we rolled our own schnorr!");
     /// let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
     /// let mut r = Scalar::random(&mut rand::thread_rng());
-    /// let R = XOnly::from_scalar_mul(schnorr.G(), &mut r);
-    /// let challenge = schnorr.challenge(&R, keypair.public_key(), message);
+    /// let R = XOnly::from_scalar_mul(G, &mut r);
+    /// let challenge = schnorr.challenge(R, keypair.public_key(), message);
     /// let s = s!(r + challenge * { keypair.secret_key() });
     /// let signature = Signature { R, s };
     /// assert!(schnorr.verify(&keypair.verification_key(), message, &signature));
@@ -176,12 +189,7 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
     ///
     /// [BIP-340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
     /// [`Secrecy`]: secp256kfun::marker::Secrecy
-    pub fn challenge<S: Secrecy>(
-        &self,
-        R: &XOnly,
-        X: &XOnly,
-        m: Message<'_, S>,
-    ) -> Scalar<S, Zero> {
+    pub fn challenge<S: Secrecy>(&self, R: XOnly, X: XOnly, m: Message<'_, S>) -> Scalar<S, Zero> {
         let hash = self.challenge_hash.clone();
         let challenge = Scalar::from_hash(hash.add(R).add(X).add(&m));
 
@@ -228,9 +236,9 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
     ) -> bool {
         let X = public_key;
         let (R, s) = signature.as_tuple();
-        let c = self.challenge(R, &X.to_xonly(), message);
-        let R_implied = g!(s * self.G - c * X).mark::<Normal>();
-        R_implied == *R
+        let c = self.challenge(R, X.to_xonly(), message);
+        let R_implied = g!(s * G - c * X).mark::<Normal>();
+        R_implied == R
     }
 
     /// _Anticipates_ a Schnorr signature given the nonce `R` that will be used ahead of time.
@@ -242,25 +250,55 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
         R: &Point<EvenY, impl Secrecy>,
         m: Message<'_, impl Secrecy>,
     ) -> Point<Jacobian, Public, Zero> {
-        let c = self.challenge(&R.to_xonly(), &X.to_xonly(), m);
+        let c = self.challenge(R.to_xonly(), X.to_xonly(), m);
         g!(R + c * X)
     }
 }
 
 #[cfg(test)]
 pub mod test {
-    use fun::nonce::Deterministic;
+    use crate::fun::nonce::Deterministic;
 
     use super::*;
-    use crate::fun::TEST_SOUNDNESS;
+    use crate::fun::proptest::prelude::*;
     #[cfg(target_arch = "wasm32")]
     use wasm_bindgen_test::wasm_bindgen_test as test;
 
     #[test]
-    fn anticipated_signature_on_should_correspond_to_actual_signature() {
-        for _ in 0..TEST_SOUNDNESS {
+    fn deterministic_nonces_for_different_message_kinds() {
+        use core::str::FromStr;
+        use sha2::Sha256;
+        let schnorr = Schnorr::<Sha256, _>::new(Deterministic::<Sha256>::default());
+        let x =
+            Scalar::from_str("18451f9e08af9530814243e202a4a977130e672079f5c14dcf15bd4dee723072")
+                .unwrap();
+        let keypair = schnorr.new_keypair(x);
+        assert_ne!(
+            schnorr.sign(&keypair, Message::<Public>::raw(b"foo")).R,
+            schnorr
+                .sign(&keypair, Message::<Public>::plain("one", b"foo"))
+                .R
+        );
+        assert_ne!(
+            schnorr
+                .sign(&keypair, Message::<Public>::plain("one", b"foo"))
+                .R,
+            schnorr
+                .sign(&keypair, Message::<Public>::plain("two", b"foo"))
+                .R
+        );
+
+        // make sure deterministic signatures don't change
+        assert_eq!(schnorr.sign(&keypair, Message::<Public>::raw(b"foo")), Signature::<Public>::from_str("fe9e5d0319d5d221988d6fd7fe1c4bedd2fb4465f592f1002f461503332a266977bb4a0b00c00d07072c796212cbea0957ebaaa5139143761c45d997ebe36cbe").unwrap());
+        assert_eq!(schnorr.sign(&keypair, Message::<Public>::plain("one", b"foo")), Signature::<Public>::from_str("2fcf6fd140bbc4048e802c62f028e24f6534e0d15d450963265b67eead774d8b4aa7638bec9d70aa60b97e86bc4a60bf43ad2ff58e981ee1bba4f45ce02ff2c0").unwrap());
+    }
+
+    proptest! {
+
+        #[test]
+        fn anticipated_signature_on_should_correspond_to_actual_signature(sk in any::<Scalar>()) {
             let schnorr = crate::test_instance!();
-            let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
+            let keypair = schnorr.new_keypair(sk);
             let msg = Message::<Public>::plain(
                 "test",
                 b"Chancellor on brink of second bailout for banks",
@@ -274,18 +312,16 @@ pub mod test {
 
             assert_eq!(
                 anticipated_signature,
-                g!(signature.s * schnorr.G),
+                g!(signature.s * G),
                 "should anticipate the same value as actual signature"
             )
         }
-    }
 
-    #[test]
-    fn sign_deterministic() {
-        let schnorr = crate::test_instance!();
-        for _ in 0..TEST_SOUNDNESS {
-            let keypair_1 = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
-            let keypair_2 = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
+        #[test]
+        fn sign_deterministic(s1 in any::<Scalar>(), s2 in any::<Scalar>()) {
+            let schnorr = crate::test_instance!();
+            let keypair_1 = schnorr.new_keypair(s1);
+            let keypair_2 = schnorr.new_keypair(s2);
             let msg_atkdwn = Message::<Public>::plain("test", b"attack at dawn");
             let msg_rtrtnoon = Message::<Public>::plain("test", b"retreat at noon");
             let signature_1 = schnorr.sign(&keypair_1, msg_atkdwn);
@@ -295,37 +331,10 @@ pub mod test {
 
             assert!(schnorr.verify(&keypair_1.verification_key(), msg_atkdwn, &signature_1));
             assert_eq!(signature_1, signature_2);
-            assert_ne!(signature_3.R, signature_1.R);
-            assert_ne!(signature_1.R, signature_4.R);
+            if keypair_1 != keypair_2 {
+                assert_ne!(signature_3.R, signature_1.R);
+                assert_ne!(signature_1.R, signature_4.R);
+            }
         }
     }
-
-    #[test]
-    fn deterministic_nonces_for_different_message_kinds() {
-        use core::str::FromStr;
-        use sha2::Sha256;
-        let schnorr = Schnorr::<Sha256, _>::new(Deterministic::<Sha256>::default());
-        let x =
-            Scalar::from_str("18451f9e08af9530814243e202a4a977130e672079f5c14dcf15bd4dee723072")
-                .unwrap();
-        let keypair = schnorr.new_keypair(x);
-        assert_ne!(
-            schnorr.sign(&keypair, Message::<Public>::raw(b"foo")).R,
-            schnorr
-                .sign(&keypair, Message::<Public>::plain("one", b"foo"))
-                .R
-        );
-        assert_ne!(
-            schnorr
-                .sign(&keypair, Message::<Public>::plain("one", b"foo"))
-                .R,
-            schnorr
-                .sign(&keypair, Message::<Public>::plain("two", b"foo"))
-                .R
-        );
-
-        // make sure deterministic signatures don't change
-        assert_eq!(schnorr.sign(&keypair, Message::<Public>::raw(b"foo")), Signature::<Public>::from_str("fe9e5d0319d5d221988d6fd7fe1c4bedd2fb4465f592f1002f461503332a266977bb4a0b00c00d07072c796212cbea0957ebaaa5139143761c45d997ebe36cbe").unwrap());
-        assert_eq!(schnorr.sign(&keypair, Message::<Public>::plain("one", b"foo")), Signature::<Public>::from_str("2fcf6fd140bbc4048e802c62f028e24f6534e0d15d450963265b67eead774d8b4aa7638bec9d70aa60b97e86bc4a60bf43ad2ff58e981ee1bba4f45ce02ff2c0").unwrap());
-    }
 }
diff --git a/schnorr_fun/src/signature.rs b/schnorr_fun/src/signature.rs
index 0ff08634..9257399a 100644
--- a/schnorr_fun/src/signature.rs
+++ b/schnorr_fun/src/signature.rs
@@ -47,8 +47,8 @@ impl<S> Signature<S> {
     /// # let signature = schnorr_fun::Signature::random(&mut rand::thread_rng());
     /// let (R, s) = signature.as_tuple();
     /// ```
-    pub fn as_tuple(&self) -> (&XOnly, &Scalar<S, Zero>) {
-        (&self.R, &self.s)
+    pub fn as_tuple(&self) -> (XOnly, &Scalar<S, Zero>) {
+        (self.R, &self.s)
     }
 
     /// Marks the signature with a [`Secrecy`]. If it is marked as `Secret` the
diff --git a/schnorr_fun/tests/bip340.rs b/schnorr_fun/tests/bip340.rs
index dd0b2673..5bb20409 100644
--- a/schnorr_fun/tests/bip340.rs
+++ b/schnorr_fun/tests/bip340.rs
@@ -46,11 +46,11 @@ fn signing_test_vectors() {
         let line: Vec<&str> = line.split(',').collect();
         let aux_bytes = hex::decode(line[3]).unwrap();
         let fake_rng = AuxRng(&aux_bytes[..]);
-        let bip340 = Schnorr::<Sha256, _, _>::new(Synthetic::<Sha256, _>::new(fake_rng));
+        let bip340 = Schnorr::<Sha256, _>::new(Synthetic::<Sha256, _>::new(fake_rng));
         let secret_key = Scalar::<Secret, NonZero>::from_str(line[1]).unwrap();
         let expected_public_key = XOnly::from_str(line[2]).unwrap();
         let keypair = bip340.new_keypair(secret_key);
-        assert_eq!(keypair.public_key(), &expected_public_key);
+        assert_eq!(keypair.public_key(), expected_public_key);
         let message = hex::decode(line[4]).unwrap();
         let signature = bip340.sign(&keypair, Message::<Public>::raw(&message));
         let expected_signature = Signature::<Public>::from_str(line[5]).unwrap();
diff --git a/secp256kfun/Cargo.toml b/secp256kfun/Cargo.toml
index 280a8306..38bb75c8 100644
--- a/secp256kfun/Cargo.toml
+++ b/secp256kfun/Cargo.toml
@@ -20,8 +20,8 @@ digest = "0.9"
 subtle = { package = "subtle-ng", version = "2" }
 rand_core = { version = "0.6" }
 serde_crate = { package = "serde", version = "1.0",  optional = true, default-features = false, features = ["derive"] }
-# secp256kfun_k256_backend = { path = "../../k256_backend/k256", features = ["expose-field"] }
-secp256kfun_k256_backend = {  version = "1.0.0", features = ["expose-field"] }
+# secp256kfun_k256_backend = { path = "../../k256_backend/k256" }
+secp256kfun_k256_backend = {  version = "2.0.0" }
 secp256kfun_parity_backend = { version = "0.1.5", optional = true }
 secp256k1 = { version = "0.20", optional = true, default-features = false }
 proptest = { version = "1", optional = true }
@@ -48,7 +48,7 @@ wasm-bindgen-test = "0.3"
 [features]
 default = ["std"]
 all = ["std", "serde", "libsecp_compat", "nightly"]
-alloc = ["serde_crate/alloc"]
+alloc = ["serde_crate/alloc", "secp256kfun_k256_backend/alloc"]
 std = ["alloc"]
 libsecp_compat = ["secp256k1"]
 serde = [ "serde_crate" ]
diff --git a/secp256kfun/src/backend/k256.rs b/secp256kfun/src/backend/k256.rs
index bac59df9..00f526ea 100644
--- a/secp256kfun/src/backend/k256.rs
+++ b/secp256kfun/src/backend/k256.rs
@@ -310,6 +310,13 @@ impl TimeSensitive for ConstantTime {
     fn xonly_eq(lhs: &XOnly, rhs: &XOnly) -> bool {
         lhs.0.ct_eq(&rhs.0).into()
     }
+
+    fn lincomb_iter<'a, 'b, A: Iterator<Item = &'a Point>, B: Iterator<Item = &'b Scalar>>(
+        points: A,
+        scalars: B,
+    ) -> Point {
+        secp256kfun_k256_backend::lincomb_iter(points, scalars)
+    }
 }
 
 pub struct VariableTime;
@@ -439,6 +446,14 @@ impl TimeSensitive for VariableTime {
     fn point_double_mul(x: &Scalar, A: &Point, y: &Scalar, B: &Point) -> Point {
         ConstantTime::point_double_mul(x, A, y, B)
     }
+
+    #[cfg(feature = "alloc")]
+    fn lincomb_iter<'a, 'b, A: Iterator<Item = &'a Point>, B: Iterator<Item = &'b Scalar>>(
+        points: A,
+        scalars: B,
+    ) -> Point {
+        ConstantTime::lincomb_iter(points, scalars)
+    }
 }
 
 impl VariableTime {
diff --git a/secp256kfun/src/backend/mod.rs b/secp256kfun/src/backend/mod.rs
index 601863d8..cd4f28d6 100644
--- a/secp256kfun/src/backend/mod.rs
+++ b/secp256kfun/src/backend/mod.rs
@@ -76,4 +76,12 @@ pub trait TimeSensitive {
     fn scalar_invert(scalar: &Scalar) -> Scalar;
     fn scalar_mul_basepoint(scalar: &Scalar, base: &BasePoint) -> Point;
     fn xonly_eq(lhs: &XOnly, rhs: &XOnly) -> bool;
+    fn lincomb_iter<'a, 'b, A: Iterator<Item = &'a Point>, B: Iterator<Item = &'b Scalar>>(
+        points: A,
+        scalars: B,
+    ) -> Point {
+        points.zip(scalars).fold(Point::zero(), |acc, (X, k)| {
+            Self::point_add_point(&acc, &Self::scalar_mul_point(k, X))
+        })
+    }
 }
diff --git a/secp256kfun/src/hash.rs b/secp256kfun/src/hash.rs
index 8e9f4ff3..e23fbd09 100644
--- a/secp256kfun/src/hash.rs
+++ b/secp256kfun/src/hash.rs
@@ -56,42 +56,68 @@ where
 /// ```
 /// use digest::Digest;
 /// use secp256kfun::hash::{HashAdd, HashInto};
+/// #[derive(Clone, Copy)]
 /// struct CryptoData([u8; 42]);
 ///
 /// impl HashInto for CryptoData {
-///     fn hash_into(&self, hash: &mut impl digest::Digest) {
+///     fn hash_into(self, hash: &mut impl digest::Digest) {
 ///         hash.update(&self.0[..])
 ///     }
 /// }
 ///
 /// let cryptodata = CryptoData([42u8; 42]);
-/// let hash = sha2::Sha256::default().add(&cryptodata).finalize();
+/// let hash = sha2::Sha256::default().add(cryptodata).finalize();
 /// ```
 pub trait HashInto {
     /// Asks the item to convert itself to bytes and add itself to `hash`.
-    fn hash_into(&self, hash: &mut impl digest::Digest);
+    fn hash_into(self, hash: &mut impl digest::Digest);
 }
 
-impl HashInto for [u8] {
-    fn hash_into(&self, hash: &mut impl digest::Digest) {
-        hash.update(self)
+impl HashInto for u8 {
+    fn hash_into(self, hash: &mut impl digest::Digest) {
+        hash.update(&[self])
     }
 }
 
-impl HashInto for str {
-    fn hash_into(&self, hash: &mut impl digest::Digest) {
+impl<'a, T: HashInto + Clone> HashInto for &'a T {
+    fn hash_into(self, hash: &mut impl digest::Digest) {
+        self.clone().hash_into(hash)
+    }
+}
+
+impl<'a, T> HashInto for &'a [T]
+where
+    &'a T: HashInto,
+{
+    fn hash_into(self, hash: &mut impl digest::Digest) {
+        for item in self {
+            item.hash_into(hash)
+        }
+    }
+}
+
+impl HashInto for &str {
+    fn hash_into(self, hash: &mut impl digest::Digest) {
         hash.update(self.as_bytes())
     }
 }
 
+impl<T: HashInto, const N: usize> HashInto for [T; N] {
+    fn hash_into(self, hash: &mut impl digest::Digest) {
+        for item in self {
+            item.hash_into(hash)
+        }
+    }
+}
+
 /// Extension trait for [`digest::Digest`] to make adding things to the hash convenient.
 pub trait HashAdd {
     /// Converts something that implements [`HashInto`] to bytes and then incorporate the result into the digest (`self`).
-    fn add<HI: HashInto + ?Sized>(self, data: &HI) -> Self;
+    fn add<HI: HashInto>(self, data: HI) -> Self;
 }
 
 impl<D: Digest> HashAdd for D {
-    fn add<HI: HashInto + ?Sized>(mut self, data: &HI) -> Self {
+    fn add<HI: HashInto>(mut self, data: HI) -> Self {
         data.hash_into(&mut self);
         self
     }
diff --git a/secp256kfun/src/macros.rs b/secp256kfun/src/macros.rs
index a051e49c..506558d0 100644
--- a/secp256kfun/src/macros.rs
+++ b/secp256kfun/src/macros.rs
@@ -321,7 +321,7 @@ macro_rules! derive_nonce {
         use core::borrow::Borrow;
         use $crate::nonce::NonceGen;
         Scalar::from_hash(
-            $nonce_gen.begin_derivation($secret.borrow())$(.add($public.borrow()))+
+            $nonce_gen.begin_derivation($secret.borrow())$(.add($public))+
         )
     }}
 }
diff --git a/secp256kfun/src/op.rs b/secp256kfun/src/op.rs
index 156a3fd2..5457d73b 100644
--- a/secp256kfun/src/op.rs
+++ b/secp256kfun/src/op.rs
@@ -85,6 +85,20 @@ pub fn point_add<Z1, Z2, S1, S2, T1, T2>(
     Point::from_inner(PointBinary::add(A, B), Jacobian)
 }
 
+/// Does a linear combination of points
+pub fn lincomb<'a, T1: 'a, S1: 'a, Z1: 'a, S2: 'a, Z2: 'a>(
+    scalars: impl IntoIterator<Item = &'a Scalar<S2, Z2>>,
+    points: impl IntoIterator<Item = &'a Point<T1, S1, Z1>>,
+) -> Point<Jacobian, Public, Zero> {
+    Point::from_inner(
+        ConstantTime::lincomb_iter(
+            points.into_iter().map(|p| &p.0),
+            scalars.into_iter().map(|s| &s.0),
+        ),
+        Jacobian,
+    )
+}
+
 pub(crate) trait PointBinary<T2, S2, Z2> {
     fn add(&self, rhs: &Point<T2, S2, Z2>) -> backend::Point;
     fn sub(&self, rhs: &Point<T2, S2, Z2>) -> backend::Point;
@@ -630,4 +644,22 @@ mod test {
         .unwrap();
         assert_eq!(R_implied, R_expected);
     }
+
+    use proptest::prelude::*;
+
+    proptest! {
+
+        #[test]
+        fn lincomb_against_mul(a in any::<Scalar>(),
+                               b in any::<Scalar>(),
+                               c in any::<Scalar>(),
+                               A in any::<Point>(),
+                               B in any::<Point>(),
+                               C in any::<Point>()
+        ) {
+            use crate::op::*;
+            assert_eq!(lincomb([&a,&b,&c], [&A,&B,&C]),
+                       point_add(&scalar_mul_point(&a, &A), &point_add(&scalar_mul_point(&b, &B), &scalar_mul_point(&c, &C))))
+        }
+    }
 }
diff --git a/secp256kfun/src/point.rs b/secp256kfun/src/point.rs
index 7d62d896..3b0efdf7 100644
--- a/secp256kfun/src/point.rs
+++ b/secp256kfun/src/point.rs
@@ -387,7 +387,7 @@ impl<S, T: Normalized> Point<T, S, NonZero> {
 }
 
 impl<T: Normalized, S> HashInto for Point<T, S, NonZero> {
-    fn hash_into(&self, hash: &mut impl digest::Digest) {
+    fn hash_into(self, hash: &mut impl digest::Digest) {
         hash.update(self.to_bytes().as_ref())
     }
 }
diff --git a/secp256kfun/src/scalar.rs b/secp256kfun/src/scalar.rs
index d6772581..6c50b7c2 100644
--- a/secp256kfun/src/scalar.rs
+++ b/secp256kfun/src/scalar.rs
@@ -49,6 +49,8 @@ use rand_core::{CryptoRng, RngCore};
 #[derive(Clone, Eq)]
 pub struct Scalar<S = Secret, Z = NonZero>(pub(crate) backend::Scalar, PhantomData<(Z, S)>);
 
+impl<Z: Clone> Copy for Scalar<Public, Z> {}
+
 impl<Z> core::hash::Hash for Scalar<Public, Z> {
     fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
         self.to_bytes().hash(state)
@@ -314,8 +316,8 @@ impl<S, Z> core::ops::Neg for &Scalar<S, Z> {
     }
 }
 
-impl HashInto for Scalar {
-    fn hash_into(&self, hash: &mut impl digest::Digest) {
+impl<S, Z> HashInto for Scalar<S, Z> {
+    fn hash_into(self, hash: &mut impl digest::Digest) {
         hash.update(&self.to_bytes())
     }
 }
diff --git a/secp256kfun/src/slice.rs b/secp256kfun/src/slice.rs
index 00c41fdc..3b768dc0 100644
--- a/secp256kfun/src/slice.rs
+++ b/secp256kfun/src/slice.rs
@@ -77,7 +77,7 @@ impl<'a, S> From<Slice<'a, S>> for &'a [u8] {
 }
 
 impl<'a, S> HashInto for Slice<'a, S> {
-    fn hash_into(&self, hash: &mut impl digest::Digest) {
+    fn hash_into(self, hash: &mut impl digest::Digest) {
         hash.update(self.inner)
     }
 }
diff --git a/secp256kfun/src/xonly.rs b/secp256kfun/src/xonly.rs
index b2888cfa..5aca7ae7 100644
--- a/secp256kfun/src/xonly.rs
+++ b/secp256kfun/src/xonly.rs
@@ -117,7 +117,7 @@ impl XOnly {
 }
 
 impl HashInto for XOnly {
-    fn hash_into(&self, hash: &mut impl digest::Digest) {
+    fn hash_into(self, hash: &mut impl digest::Digest) {
         hash.update(self.as_bytes())
     }
 }