packages feed

musig2 0.1.0 → 0.1.1

raw patch · 17 files changed

+145/−115 lines, 17 filesdep ~ppad-secp256k1

Dependency ranges changed: ppad-secp256k1

Files

CHANGELOG view
@@ -1,5 +1,9 @@ # Changelog +## 0.1.1 (2025-12-22)++* Bump `ppad-secp256k1` to 0.5.0. More efficient and faster EC operations.+ ## 0.1.0 (2025-09-20) -* Initial release, full BIP327 support except [Deterministic and Stateless Signing for a Single Signer](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki#user-content-Deterministic_and_Stateless_Signing_for_a_Single_Signer)+* Initial release, full BIP-0327 support except [Deterministic and Stateless Signing for a Single Signer](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki#user-content-Deterministic_and_Stateless_Signing_for_a_Single_Signer)
lib/Crypto/Curve/Secp256k1/MuSig2.hs view
@@ -37,8 +37,8 @@ > let sec2 = MuSig2.SecKey 0x68E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF > > -- derive public keys-> let pub1 = Secp256k1.derive_pub 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF-> let pub2 = Secp256k1.derive_pub 0x68E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF+> let Just pub1 = Secp256k1.derive_pub 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF+> let Just pub2 = Secp256k1.derive_pub 0x68E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF > let pubkeys = [pub1, pub2] > > -- create key aggregation context@@ -188,7 +188,7 @@     -- gaccVal == 1 means no negation, gaccVal == n-1 means negation     parityFromGacc = gaccVal /= 1     d = if parityFromGacc /= oddAggPk then _CURVE_Q - d' else d'-    p = derive_pub d' -- Use original secret key for public key derivation+    p = fromMaybe (error "musig2 (sign): failed to derive public key") $ derive_pub d' -- Use original secret key for public key derivation     a = computeKeyAggCoef p publicKeys     -- if has_even_Y(R):     --   k = k1 + b*k2@@ -198,7 +198,9 @@     b = getSigningNonceCoeff ctx     k = if isEvenPub nonce then k1 + b * k2 else _CURVE_Q - (k1 + b * k2)     s = modQ (k + e * a * d)-    pubNonce' = PubNonce (mul _CURVE_G secnonce.k1) (mul _CURVE_G secnonce.k2)+    r1' = fromMaybe (error "musig2 (sign): failed to compute r1") $ mul _CURVE_G secnonce.k1+    r2' = fromMaybe (error "musig2 (sign): failed to compute r2") $ mul _CURVE_G secnonce.k2+    pubNonce' = PubNonce r1' r2'    in     if partialSigVerifyInternal s pubNonce' p ctx then s else error "musig2 (sign): could not verify partial signature against public nonce, public key and session context" @@ -264,7 +266,8 @@     finalNonce = getSigningNonce ctx -- This is the final aggregate nonce used for evenness check     s = if partial < 0 || partial >= _CURVE_Q then error "musig2 (partialSigVerifyInternal): partial signature must be within curve order." else partial     -- Reconstruct the individual's effective nonce: R_s1 + b * R_s2-    re' = add r1' $ mul r2' b+    r2b = fromMaybe (error "musig2 (partialSigVerifyInternal): failed to compute r2 * b") $ mul r2' b+    re' = add r1' r2b     -- Negate individual nonce if final aggregate nonce has odd Y     re = if isEvenPub finalNonce then re' else neg re'     a = computeKeyAggCoef pk publicKeys@@ -272,8 +275,9 @@     g = if oddAggPk then _CURVE_Q - 1 else 1     -- Apply parity accumulator: gacc is accumulated parity factor     g' = modQ (g * gaccVal)-    sG = mul _CURVE_G s-    sG' = re `add` mul pk (modQ (e * a * g'))+    sG = fromMaybe (error "musig2 (partialSigVerifyInternal): failed to compute s * G") $ mul _CURVE_G s+    pkMul = fromMaybe (error "musig2 (partialSigVerifyInternal): failed to compute pk multiplication") $ mul pk (modQ (e * a * g'))+    sG' = re `add` pkMul    in     sG == sG' @@ -394,7 +398,7 @@   checkOrder = (>= _CURVE_Q) . getTweak  {- | Gets the signing nonce as a 'Projective' following-[BIP327 algorithm and recommendations](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki#dealing-with-infinity-in-nonce-aggregation).+[BIP-0327 algorithm and recommendations](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki#dealing-with-infinity-in-nonce-aggregation). -} getSigningNonce :: SessionContext -> Projective getSigningNonce ctx =@@ -402,12 +406,13 @@     b = getSigningNonceCoeff ctx     aggNonce = ctx.aggNonce     aggNonce' = if aggNonce.r1 == _CURVE_ZERO then PubNonce _CURVE_G aggNonce.r2 else aggNonce-    finalNonce = add aggNonce'.r1 (mul aggNonce'.r2 b)+    r2b = fromMaybe (error "musig2 (getSigningNonce): failed to compute r2 * b") $ mul aggNonce'.r2 b+    finalNonce = add aggNonce'.r1 r2b    in     if finalNonce == _CURVE_ZERO then _CURVE_G else finalNonce  {- | Gets the signing nonce coefficient following-[BIP327 algorithm and recommendations](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki#dealing-with-infinity-in-nonce-aggregation).+[BIP-0327 algorithm and recommendations](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki#dealing-with-infinity-in-nonce-aggregation). -} getSigningNonceCoeff :: SessionContext -> Integer getSigningNonceCoeff ctx =@@ -428,11 +433,11 @@     bytesToInteger $ hashTagModQ "MuSig/noncecoef" preimage  {- | Gets the signing challenge hash as a 'ByteString' following-[BIP327 algorithm](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).+[BIP-0327 algorithm](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).  Note that the signing challenge hash is the naming convention from the [MuSig2 paper, page 6](https://eprint.iacr.org/2020/1261).-In the BIP327 it is referred as @e@.+In the BIP-0327 it is referred as @e@. -} getSigningHash :: SessionContext -> ByteString getSigningHash ctx =@@ -453,11 +458,13 @@  -- | Tweak that can be added to an aggregated 'Pub'key. data Tweak-  = -- | X-only tweak required by Taproot tweaking to add script paths to a Taproot output.-    -- See [BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki).+  = {- | X-only tweak required by Taproot tweaking to add script paths to a Taproot output.+    See [BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki).+    -}     XOnlyTweak !Integer-  | -- | Plain tweak that can be used to derive child aggregated 'Pub'keys per-    -- [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)+  | {- | Plain tweak that can be used to derive child aggregated 'Pub'keys per+    [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)+    -}     PlainTweak !Integer   deriving (Read, Show, Eq, Ord, Generic) @@ -466,7 +473,7 @@ getTweak (XOnlyTweak int) = int getTweak (PlainTweak int) = int --- | Applies a tweak to a KeyAggContext and returns a new KeyAggContext following [BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).+-- | Applies a tweak to a KeyAggContext and returns a new KeyAggContext following [BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). applyTweak :: KeyAggContext -> Tweak -> KeyAggContext applyTweak ctx newTweak =   let pubkey = q ctx@@ -477,7 +484,9 @@         PlainTweak t ->           -- Plain tweak: g = 1, Q' = g*Q + t*G, tacc' = t + g*tacc, gacc' = g*gacc           let g = 1-              tweakedPk = add (mul pubkey g) (mul _CURVE_G t)+              pubkeyMul = fromMaybe (error "musig2 (applyTweak): failed to compute pubkey * g") $ mul pubkey g+              tG = fromMaybe (error "musig2 (applyTweak): failed to compute t * G") $ mul _CURVE_G t+              tweakedPk = add pubkeyMul tG               newAccTweak = modQ (t + (g * accTweakVal))               newGacc = modQ (g * gaccIn)            in if tweakedPk == _CURVE_ZERO@@ -486,7 +495,9 @@         XOnlyTweak t ->           -- X-only tweak: g = 1 if even Y, g = n-1 if odd Y, tacc' = t + g*tacc           let g = if isEvenPub pubkey then 1 else _CURVE_Q - 1-              tweakedPk = add (mul pubkey g) (mul _CURVE_G t)+              pubkeyMul = fromMaybe (error "musig2 (applyTweak): failed to compute pubkey * g") $ mul pubkey g+              tG = fromMaybe (error "musig2 (applyTweak): failed to compute t * G") $ mul _CURVE_G t+              tweakedPk = add pubkeyMul tG               newAccTweak = modQ (t + (g * accTweakVal))               newGacc = modQ (g * gaccIn)            in if tweakedPk == _CURVE_ZERO@@ -523,7 +534,7 @@ compute the private key used to create both signatures.  If you want to follow-[BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki)+[BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki) suggestions, then use 'secNonceGen' otherwise use 'mkSecNonce'. -} data SecNonce = SecNonce@@ -544,7 +555,7 @@ this function.  Note that this does not follow the-[BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki)+[BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki) algorithm. -} mkSecNonce :: IO SecNonce@@ -581,7 +592,16 @@     }  {- | Generates a 'SecNonce' using the inputs and algorithms from-[BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).+[BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).++Tries to get the entropy from the system's underlying Cryptographic Secure+Pseudorandom Number Generator (CSPRNG) using the+[@entropy@](https://hackage.haskell.org/package/entropy) package.++== WARNING++Make sure that you have access to a good CSPRNG in your system before calling+this function. -} secNonceGen :: SecNonceGenParams -> IO SecNonce secNonceGen params = loop@@ -596,7 +616,7 @@  {- | Generates a 'SecNonce' using a given random 'ByteString' and the inputs and algorithms from-[BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).+[BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).  == WARNING @@ -664,7 +684,10 @@  -- | Generates a 'PubNonce' from a 'SecNonce'. publicNonce :: SecNonce -> PubNonce-publicNonce secNonce = PubNonce (mul _CURVE_G (k1 secNonce)) (mul _CURVE_G (k2 secNonce))+publicNonce secNonce =+  let r1' = fromMaybe (error "musig2 (publicNonce): failed to compute r1") $ mul _CURVE_G (k1 secNonce)+      r2' = fromMaybe (error "musig2 (publicNonce): failed to compute r2") $ mul _CURVE_G (k2 secNonce)+   in PubNonce r1' r2'  -- | 'Data.Semigroup' implementation of 'PubNonce' for algebraic sound combination of public nonces. instance Semigroup PubNonce where@@ -680,7 +703,7 @@   mempty = PubNonce _CURVE_ZERO _CURVE_ZERO  {- | Aggregates a 'Traversable' of 'PubNonce's using the-[Nonce Aggregation algorithm in BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).+[Nonce Aggregation algorithm in BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). -} aggNonces :: (Traversable t) => t PubNonce -> Maybe PubNonce aggNonces nonces
lib/Crypto/Curve/Secp256k1/MuSig2/Internal.hs view
@@ -44,7 +44,7 @@ import Data.Traversable ()  {- | Aggregates a 'Traversable' of 'Pub'keys using the-[Key Aggregation algorithm in BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).+[Key Aggregation algorithm in BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).  The algorith can be briefly described as @@ -63,7 +63,9 @@ aggPublicKeys :: (Traversable t) => t Pub -> Maybe Pub aggPublicKeys pks   | Seq.null pksSeq = Nothing-  | otherwise = Just $ fold1WithDefault _CURVE_ZERO (Seq.zipWith aggPk coefs pksSeq)+  | otherwise = do+      mulResults <- traverse (uncurry aggPk) (Seq.zip coefs pksSeq)+      pure $ fold1WithDefault _CURVE_ZERO mulResults  where   pksSeq = Seq.fromList (toList pks)   coefs = fmap (`computeKeyAggCoef` pksSeq) pksSeq@@ -104,7 +106,7 @@  where   byteStrings = fmap serialize_point ps -{- | Tagged hashes used in [BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).+{- | Tagged hashes used in [BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).  Takes a tag and a string. -}@@ -113,7 +115,7 @@  where   taggedHash = hash t -{- | Tagged hashes used in [BIP327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki)+{- | Tagged hashes used in [BIP-0327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki) modulo the curve order.  Takes a tag and a string.
musig2.cabal view
@@ -1,6 +1,6 @@ cabal-version:   3.0 name:            musig2-version:         0.1.0+version:         0.1.1 synopsis:        MuSig2 library license:         MIT license-file:    LICENSE@@ -9,7 +9,7 @@ homepage:        https://github.com/storopoli/musig2 category:        Cryptography build-type:      Simple-tested-with:     GHC ==9.8.4+tested-with:     GHC ==9.8.4 || ==9.10.3 extra-doc-files: CHANGELOG description:   Pure BIP0327 MuSig2 (partial)signatures with tweak support on the elliptic curve secp256k1.@@ -31,7 +31,7 @@     , bytestring         >=0.9  && <0.13     , containers         >=0.6  && <0.9     , entropy            >=0.4  && <0.5-    , ppad-secp256k1     >=0.3  && <0.5+    , ppad-secp256k1     >=0.3  && <0.6     , ppad-sha256        >=0.2  && <0.3.0  test-suite musig2-tests
test/AggNonces.hs view
@@ -9,7 +9,7 @@ import Test.Tasty.HUnit import Util (parsePoint, parsePubNonce) --- | Input 'PubNonce's from BIP327 test vectors+-- | Input 'PubNonce's from BIP-0327 test vectors inputPubNonces :: [PubNonce] inputPubNonces =   map@@ -31,13 +31,13 @@ -- | Creates test case from vector data. makeTestCase :: Int -> ([Int], PubNonce) -> TestTree makeTestCase i (indices, expected) =-  testCase ("BIP327 test vector " <> show (i + 1)) $+  testCase ("BIP-0327 test vector " <> show (i + 1)) $     fromJust aggNonce @=? expected  where   selectedNonces = map (inputPubNonces !!) indices   aggNonce = aggNonces selectedNonces --- | Test vectors from [BIP327 `nonce_agg_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/nonce_agg_vectors.json).+-- | Test vectors from [BIP-0327 `nonce_agg_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/nonce_agg_vectors.json). testAggNonces :: TestTree testAggNonces =   testGroup "aggregating pubkeys" $
test/AggPartials.hs view
@@ -24,7 +24,7 @@   }   deriving (Show) --- | Global test data from BIP327 sig_agg_vectors.json+-- | Global test data from BIP-0327 @sig_agg_vectors.json@ pubkeys :: [Pub] pubkeys =   [ parsePoint "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"@@ -43,7 +43,7 @@   , parsePubNonce "02D97DDA5988461DF58C5897444F116A7C74E5711BF77A9446E27806563F3B6C47020CBAD9C363A7737F99FA06B6BE093CEAFF5397316C5AC46915C43767AE867C00"   ] --- | Raw tweak values from BIP327 test vectors+-- | Raw tweak values from BIP-0327 test vectors tweakValues :: [Integer] tweakValues =   [ parseScalar "B511DA492182A91B0FFB9A98020D55F260AE86D7ECBD0399C7383D59A5F2AF7C"@@ -67,7 +67,7 @@ msg :: ByteString msg = decodeHex "599C67EA410D005B9DA90817CF03ED3B1C868E4DA4EDF00A5880B0082C237869" --- | Test vectors - valid cases from BIP327 JSON+-- | Test vectors - valid cases from BIP-0327 JSON validTestVectors :: [SigAggTestVector] validTestVectors =   [ SigAggTestVector@@ -112,7 +112,7 @@       }   ] --- | Test vectors - error cases from BIP327 JSON+-- | Test vectors - error cases from BIP-0327 JSON errorTestVectors :: [SigAggTestVector] errorTestVectors =   [ SigAggTestVector@@ -138,7 +138,7 @@ -- | Creates a test case for valid signature aggregation. makeValidTestCase :: Int -> SigAggTestVector -> TestTree makeValidTestCase i SigAggTestVector{..} =-  testCase ("BIP327 SigAgg Valid Vector " ++ show (i + 1)) $ do+  testCase ("BIP-0327 SigAgg Valid Vector " ++ show (i + 1)) $ do     let selectedKeys = map (pubkeys !!) keyIndices     let selectedTweaks = buildTweaks tweakIndices isXOnly     let selectedPsigs = map (psigs !!) psigIndices@@ -151,7 +151,7 @@ -- | Creates a test case for error signature aggregation. makeErrorTestCase :: Int -> SigAggTestVector -> TestTree makeErrorTestCase i SigAggTestVector{..} =-  testCase ("BIP327 SigAgg Error Vector " ++ show (i + 1)) $ do+  testCase ("BIP-0327 SigAgg Error Vector " ++ show (i + 1)) $ do     -- For the error case, we expect aggPartials to fail due to invalid partial signature     -- The test vector has psig[8] which is FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141     -- This exceeds the curve order and should cause validation to fail@@ -161,7 +161,7 @@ testAggPartials :: TestTree testAggPartials =   testGroup-    "BIP327 Signature Aggregation Vectors"+    "BIP-0327 Signature Aggregation Vectors"     [ testGroup "Valid Cases" $ zipWith makeValidTestCase [0 ..] validTestVectors     , testGroup "Error Cases" $ zipWith makeErrorTestCase [0 ..] errorTestVectors     ]
test/AggPartialsProperty.hs view
@@ -6,7 +6,7 @@ import Crypto.Curve.Secp256k1.MuSig2 (SecKey (..), SecNonce (..), aggNonces, aggPartials, mkSessionContext, partialSigVerify, publicNonce, sign) import Data.ByteString (ByteString) import qualified Data.ByteString as BS-import Data.Maybe (fromJust)+import Data.Maybe (fromJust, fromMaybe) import Test.Tasty import Test.Tasty.QuickCheck as QC import Util ()@@ -24,8 +24,8 @@ -- | Property: Aggregating valid partial signatures produces a consistent result prop_aggValidPartials :: SecNonce -> SecKey -> SecNonce -> SecKey -> ByteString -> Property prop_aggValidPartials secNonce1 secKey1 secNonce2 secKey2 msg =-  let pubkey1 = derive_pub (case secKey1 of SecKey sk -> sk)-      pubkey2 = derive_pub (case secKey2 of SecKey sk -> sk)+  let pubkey1 = fromMaybe (error "Failed to derive pubkey1") $ derive_pub (case secKey1 of SecKey sk -> sk)+      pubkey2 = fromMaybe (error "Failed to derive pubkey2") $ derive_pub (case secKey2 of SecKey sk -> sk)       pubkeys = [pubkey1, pubkey2]       pubNonces = [publicNonce secNonce1, publicNonce secNonce2]       aggNonce = fromJust $ aggNonces pubNonces@@ -38,8 +38,8 @@ -- | Property: Aggregation is deterministic - same inputs produce same output prop_aggDeterministic :: SecNonce -> SecKey -> SecNonce -> SecKey -> ByteString -> Property prop_aggDeterministic secNonce1 secKey1 secNonce2 secKey2 msg =-  let pubkey1 = derive_pub (case secKey1 of SecKey sk -> sk)-      pubkey2 = derive_pub (case secKey2 of SecKey sk -> sk)+  let pubkey1 = fromMaybe (error "Failed to derive pubkey1") $ derive_pub (case secKey1 of SecKey sk -> sk)+      pubkey2 = fromMaybe (error "Failed to derive pubkey2") $ derive_pub (case secKey2 of SecKey sk -> sk)       pubkeys = [pubkey1, pubkey2]       pubNonces = [publicNonce secNonce1, publicNonce secNonce2]       aggNonce = fromJust $ aggNonces pubNonces@@ -54,8 +54,8 @@ -- | Property: All partial signatures should verify individually before aggregation prop_partialsVerifyBeforeAgg :: SecNonce -> SecKey -> SecNonce -> SecKey -> ByteString -> Property prop_partialsVerifyBeforeAgg secNonce1 secKey1 secNonce2 secKey2 msg =-  let pubkey1 = derive_pub (case secKey1 of SecKey sk -> sk)-      pubkey2 = derive_pub (case secKey2 of SecKey sk -> sk)+  let pubkey1 = fromMaybe (error "Failed to derive pubkey1") $ derive_pub (case secKey1 of SecKey sk -> sk)+      pubkey2 = fromMaybe (error "Failed to derive pubkey2") $ derive_pub (case secKey2 of SecKey sk -> sk)       pubkeys = [pubkey1, pubkey2]       pubNonces = [publicNonce secNonce1, publicNonce secNonce2]       aggNonce = fromJust $ aggNonces pubNonces@@ -69,7 +69,7 @@ -- | Property: For a single signer, aggregation should work correctly prop_singleSignerAgg :: SecNonce -> SecKey -> ByteString -> Property prop_singleSignerAgg secNonce secKey@(SecKey sk) msg =-  let pubkey = derive_pub sk+  let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk       pubNonce = publicNonce secNonce       pubNonces = [pubNonce]       pubkeys = [pubkey]
test/AggPubkeys.hs view
@@ -10,7 +10,7 @@ import Test.Tasty.HUnit import Util (decodeHex, extractXOnly, parsePoint) --- | Input public keys from BIP327 test vectors+-- | Input public keys from BIP-0327 test vectors inputPubkeys :: [Pub] inputPubkeys =   map@@ -33,7 +33,7 @@   , ([0, 0, 1, 1], decodeHex "69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E")   ] --- | Tweak test vectors from BIP327.+-- | Tweak test vectors from BIP-0327. tweaks :: [Integer] tweaks =   [ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 -- curve order (invalid)@@ -50,7 +50,7 @@ -- | Creates test case from vector data. makeTestCase :: Int -> ([Int], ByteString) -> TestTree makeTestCase i (indices, expected) =-  testCase ("BIP327 test vector " <> show (i + 1)) $+  testCase ("BIP-0327 test vector " <> show (i + 1)) $     extractXOnly aggPk @=? expected  where   selectedKeys = map (inputPubkeys !!) indices@@ -60,7 +60,7 @@ -- | Creates error test case from vector data. makeErrorTestCase :: Int -> ([Int], Int, Bool, String) -> TestTree makeErrorTestCase i (keyIndices, tweakIndex, isXOnly, expectedMsg) =-  testCase ("BIP327 error test vector " <> show (i + 1)) $ do+  testCase ("BIP-0327 error test vector " <> show (i + 1)) $ do     result <- try $ evaluate $ mkKeyAggContext selectedKeys (Just tweak)     case result of       Left (ErrorCall msg) -> assertBool ("Expected '" <> expectedMsg <> "' in error message, got: " <> msg) (expectedMsg `isSubsequenceOf` msg)@@ -77,7 +77,7 @@     | x == y = isSubsequenceOf xs ys     | otherwise = isSubsequenceOf (x : xs) ys --- | Test vectors from [BIP327 `key_agg_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/key_agg_vectors.json).+-- | Test vectors from [BIP-0327 `key_agg_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/key_agg_vectors.json). testAggPubkeys :: TestTree testAggPubkeys =   testGroup "aggregating pubkeys" $
test/NonceGen.hs view
@@ -26,7 +26,7 @@   , expected_r2 :: Pub   } --- | Hardcoded test vectors from bip-0327/vectors/nonce_gen_vectors.json.+-- | Hardcoded test vectors from @bip-0327/vectors/nonce_gen_vectors.json@. testVectors :: [NonceGenTestVector] testVectors =   [ NonceGenTestVector@@ -82,7 +82,7 @@ -- | Creates a test case from a test vector. makeNonceGenTestCase :: Int -> NonceGenTestVector -> TestTree makeNonceGenTestCase i NonceGenTestVector{..} =-  testCase ("BIP327 NonceGen Vector " ++ show (i + 1)) $ do+  testCase ("BIP-0327 NonceGen Vector " ++ show (i + 1)) $ do     let params =           SecNonceGenParams             { _pk = pk@@ -101,5 +101,5 @@ -- | Main test group for NonceGen. testNonceGen :: TestTree testNonceGen =-  testGroup "BIP327 NonceGen Vectors" $+  testGroup "BIP-0327 NonceGen Vectors" $     zipWith makeNonceGenTestCase [0 ..] testVectors
test/NonceGenProperty.hs view
@@ -7,6 +7,7 @@ import Crypto.Curve.Secp256k1.MuSig2 (PubNonce (..), SecKey (..), SecNonce (..), SecNonceGenParams (..), publicNonce, secNonceGenWithRand) import Crypto.Curve.Secp256k1.MuSig2.Internal (hashTag, integerToBytes32, xorByteStrings) import Data.ByteString ()+import Data.Maybe (fromMaybe) import Test.Tasty import Test.Tasty.QuickCheck as QC import Util (Rand32 (..), Scalar (..))@@ -31,7 +32,7 @@ prop_correctPubNonce (Rand32 rand) params =   let sn@SecNonce{..} = secNonceGenWithRand rand params       PubNonce r1 r2 = publicNonce sn-   in r1 === mul _CURVE_G k1 .&&. r2 === mul _CURVE_G k2+   in r1 === fromMaybe (error "Failed to multiply scalar by generator") (mul _CURVE_G k1) .&&. r2 === fromMaybe (error "Failed to multiply scalar by generator") (mul _CURVE_G k2)  -- | Property: Generating with sk provided is equivalent to XORing rand with the aux hash and generating without sk prop_skConsistency :: Rand32 -> SecNonceGenParams -> Scalar -> Property
test/ParityPub.hs view
@@ -7,7 +7,7 @@ import Test.Tasty.HUnit import Util (parsePoint) --- | Some examples from [BIP327 test vectors](https://github.com/bitcoin/bips/tree/master/bip-0327/vectors).+-- | Some examples from [BIP-0327 test vectors](https://github.com/bitcoin/bips/tree/master/bip-0327/vectors). testParityPub :: TestTree testParityPub =   testGroup
test/SignVerify.hs view
@@ -14,7 +14,7 @@ import Test.Tasty.HUnit import Util (decodeHex, parsePoint, parsePubNonce) --- | Input public keys from BIP327 test vectors+-- | Input public keys from BIP-0327 test vectors. inputPubkeys :: [Pub] inputPubkeys =   map@@ -25,7 +25,7 @@     , "020000000000000000000000000000000000000000000000000000000000000007"     ] --- | Public nonces from BIP327 test vectors+-- | Public nonces from BIP-0327 test vectors. inputPubNonces :: [PubNonce] inputPubNonces =   map@@ -37,7 +37,7 @@     , "0200000000000000000000000000000000000000000000000000000000000000090287BF891D2A6DEAEBADC909352AA9405D1428C15F4B75F04DAE642A95C2548480"     ] --- | Messages from BIP327 test vectors+-- | Messages from BIP-0327 test vectors. testMessages :: [ByteString] testMessages =   [ decodeHex "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF"@@ -70,31 +70,31 @@   , (decodeHex "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", [0, 1, 2], [0, 1, 2], 0, 0, "Signature exceeds group size")   ] --- | Valid secret key from BIP327 test vectors+-- | Valid secret key from BIP-0327 test vectors. testSecKey :: SecKey testSecKey = SecKey $ bytesToInteger $ decodeHex "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671" --- | Invalid secret nonce with k1 = 0 (from BIP327 test vectors secnonce index 1)+-- | Invalid secret nonce with k1 = 0 (from BIP-0327 test vectors secnonce index 1) invalidSecNonce :: SecNonce invalidSecNonce = SecNonce{k1 = 0, k2 = 0} --- | Creates test case from valid test vector data+-- | Creates test case from valid test vector data. makeValidTestCase :: Int -> ([Int], [Int], Int, Int, ByteString) -> TestTree makeValidTestCase i (keyIndices, nonceIndices, msgIndex, signerIndex, expectedSig) =-  testCase ("BIP327 valid test vector " <> show (i + 1)) $ do+  testCase ("BIP-0327 valid test vector " <> show (i + 1)) $ do     let selectedKeys = map (inputPubkeys !!) keyIndices         selectedNonces = map (inputPubNonces !!) nonceIndices         msg = testMessages !! msgIndex         signature = bytesToInteger expectedSig -    -- Test that the valid signature verifies+    -- Test that the valid signature verifies.     let result = partialSigVerify signature selectedNonces selectedKeys [] msg signerIndex     assertBool "Valid signature should verify" result --- | Creates test case from invalid test vector data+-- | Creates test case from invalid test vector data. makeInvalidTestCase :: Int -> (ByteString, [Int], [Int], Int, Int, String) -> TestTree makeInvalidTestCase i (sigBytes, keyIndices, nonceIndices, msgIndex, signerIndex, comment) =-  testCase ("BIP327 invalid test vector " <> show (i + 1) <> ": " <> comment) $ do+  testCase ("BIP-0327 invalid test vector " <> show (i + 1) <> ": " <> comment) $ do     let selectedKeys = map (inputPubkeys !!) keyIndices         selectedNonces = map (inputPubNonces !!) nonceIndices         msg = testMessages !! msgIndex@@ -107,7 +107,7 @@ -- | Creates test case from error test vector data makeErrorTestCase :: Int -> (ByteString, [Int], [Int], Int, Int, String) -> TestTree makeErrorTestCase i (sigBytes, keyIndices, nonceIndices, msgIndex, signerIndex, comment) =-  testCase ("BIP327 error test vector " <> show (i + 1) <> ": " <> comment) $ do+  testCase ("BIP-0327 error test vector " <> show (i + 1) <> ": " <> comment) $ do     let selectedKeys = map (inputPubkeys !!) keyIndices         selectedNonces = map (inputPubNonces !!) nonceIndices         msg = testMessages !! msgIndex@@ -119,10 +119,10 @@       Left (ErrorCall _) -> return () -- Expected error       Right _ -> assertFailure ("Expected error but verification succeeded: " <> comment) --- | Test case for invalid secret nonce (k1 = 0) from BIP327 sign_error_test_cases+-- | Test case for invalid secret nonce (k1 = 0) from BIP-0327 sign_error_test_cases testInvalidSecNonce :: TestTree testInvalidSecNonce =-  testCase "BIP327 sign error: first secnonce value is out of range" $ do+  testCase "BIP-0327 sign error: first secnonce value is out of range" $ do     let selectedKeys = take 3 inputPubkeys -- [0, 1, 2]         selectedNonces = take 3 inputPubNonces -- [0, 1, 2]         msg = head testMessages -- msg_index 0@@ -137,7 +137,7 @@           ("first secret scalar k1 is zero" `isInfixOf` errMsg)       Right _ -> assertFailure "Expected error but signing succeeded" --- | Test vectors from [BIP327 `sign_verify_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/sign_verify_vectors.json)+-- | Test vectors from [BIP-0327 `sign_verify_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/sign_verify_vectors.json) testSignVerify :: TestTree testSignVerify =   testGroup "sign and verify" $
test/SignVerifyProperty.hs view
@@ -5,7 +5,7 @@ import Crypto.Curve.Secp256k1 (derive_pub, _CURVE_Q) import Crypto.Curve.Secp256k1.MuSig2 (SecKey (..), SecNonce (..), aggNonces, mkSessionContext, partialSigVerify, publicNonce, sign) import Data.ByteString (ByteString)-import Data.Maybe (fromJust)+import Data.Maybe (fromJust, fromMaybe) import Test.Tasty import Test.Tasty.QuickCheck as QC import Util ()@@ -23,7 +23,7 @@ -- | Property: Generated signatures are in the valid range \([0, Q-1]\). prop_validSignatureRange :: SecNonce -> SecKey -> ByteString -> Property prop_validSignatureRange secNonce secKey@(SecKey sk) msg =-  let pubkey = derive_pub sk+  let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk       pubNonce = publicNonce secNonce       pubNonces = [pubNonce]       pubkeys = [pubkey]@@ -35,7 +35,7 @@ -- | Property: A signature created with sign verifies with 'partialSigVerify'. prop_signVerifyRoundtrip :: SecNonce -> SecKey -> ByteString -> Property prop_signVerifyRoundtrip secNonce secKey@(SecKey sk) msg =-  let pubkey = derive_pub sk+  let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk       pubNonce = publicNonce secNonce       pubNonces = [pubNonce]       pubkeys = [pubkey]@@ -49,7 +49,7 @@ -- | Property: Signing the same message with the same parameters produces the same signature. prop_signatureDeterminism :: SecNonce -> SecKey -> ByteString -> Property prop_signatureDeterminism secNonce secKey@(SecKey sk) msg =-  let pubkey = derive_pub sk+  let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk       pubNonce = publicNonce secNonce       pubNonces = [pubNonce]       pubkeys = [pubkey]@@ -62,7 +62,7 @@ -- | Property: Using an invalid signer index should fail verification. prop_invalidSignerIndex :: SecNonce -> SecKey -> ByteString -> Property prop_invalidSignerIndex secNonce secKey@(SecKey sk) msg =-  let pubkey = derive_pub sk+  let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk       pubNonce = publicNonce secNonce       pubNonces = [pubNonce]       pubkeys = [pubkey]
test/SignVerifyTweakProperty.hs view
@@ -5,7 +5,7 @@ import Crypto.Curve.Secp256k1 (derive_pub, _CURVE_Q) import Crypto.Curve.Secp256k1.MuSig2 (SecKey (..), SecNonce (..), Tweak (..), aggNonces, mkSessionContext, partialSigVerify, publicNonce, sign) import Data.ByteString (ByteString)-import Data.Maybe (fromJust)+import Data.Maybe (fromJust, fromMaybe) import Test.Tasty import Test.Tasty.QuickCheck import Util ()@@ -26,7 +26,7 @@ prop_validSignatureRangeWithTweaks secNonce secKey@(SecKey sk) =   forAll (resize 5 $ listOf arbitrary) $ \tweaks ->     forAll arbitrary $ \msg ->-      let pubkey = derive_pub sk+      let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk           pubNonce = publicNonce secNonce           pubNonces = [pubNonce]           pubkeys = [pubkey]@@ -40,7 +40,7 @@ prop_signVerifyRoundtripWithTweaks secNonce secKey@(SecKey sk) =   forAll (resize 5 $ listOf arbitrary) $ \tweaks ->     forAll arbitrary $ \msg ->-      let pubkey = derive_pub sk+      let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk           pubNonce = publicNonce secNonce           pubNonces = [pubNonce]           pubkeys = [pubkey]@@ -56,7 +56,7 @@ prop_signatureDeterminismWithTweaks secNonce secKey@(SecKey sk) =   forAll (resize 5 $ listOf arbitrary) $ \tweaks ->     forAll arbitrary $ \msg ->-      let pubkey = derive_pub sk+      let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk           pubNonce = publicNonce secNonce           pubNonces = [pubNonce]           pubkeys = [pubkey]@@ -69,7 +69,7 @@ -- | Property: Empty tweaks should produce the same result as no tweaks. prop_emptyTweaksEqualsNoTweaks :: SecNonce -> SecKey -> ByteString -> Property prop_emptyTweaksEqualsNoTweaks secNonce secKey@(SecKey sk) msg =-  let pubkey = derive_pub sk+  let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk       pubNonce = publicNonce secNonce       pubNonces = [pubNonce]       pubkeys = [pubkey]@@ -93,7 +93,7 @@     && t2 < _CURVE_Q     && t1       /= t2-    ==> let pubkey = derive_pub sk+    ==> let pubkey = fromMaybe (error "Failed to derive pubkey") $ derive_pub sk             pubNonce = publicNonce secNonce             pubNonces = [pubNonce]             pubkeys = [pubkey]
test/SortPubkeys.hs view
@@ -35,9 +35,9 @@       , "03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"       ] --- | Test vectors from [BIP327 `key_sort_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/key_sort_vectors.json)+-- | Test vectors from [BIP-0327 `key_sort_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/key_sort_vectors.json) testSortPubkeys :: TestTree testSortPubkeys =   testGroup     "sorting pubkeys"-    [testCase "BIP327 test vector" $ sortPublicKeys parsePoints @=? parsePointsSorted]+    [testCase "BIP-0327 test vector" $ sortPublicKeys parsePoints @=? parsePointsSorted]
test/Tweak.hs view
@@ -14,11 +14,11 @@ import Test.Tasty.HUnit import Util (decodeHex, parsePoint, parsePubNonce, parseScalar) --- | Secret key from BIP327 test vectors+-- | Secret key from BIP-0327 test vectors. testSecKey :: SecKey testSecKey = SecKey $ parseScalar "7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671" --- | Secret nonce from BIP327 test vectors+-- | Secret nonce from BIP-0327 test vectors. testSecNonce :: SecNonce testSecNonce =   SecNonce@@ -26,7 +26,7 @@     , k2 = parseScalar "FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7"     } --- | Input public keys from BIP327 test vectors+-- | Input public keys from BIP-0327 test vectors. inputPubkeys :: [Pub] inputPubkeys =   map@@ -36,7 +36,7 @@     , "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"     ] --- | Public nonces from BIP327 test vectors+-- | Public nonces from BIP-0327 test vectors. inputPubNonces :: [PubNonce] inputPubNonces =   map@@ -46,7 +46,7 @@     , "032DE2662628C90B03F5E720284EB52FF7D71F4284F627B68A853D78C78E1FFE9303E4C5524E83FFE1493B9077CF1CA6BEB2090C93D930321071AD40B2F44E599046"     ] --- | Test tweaks from BIP327 test vectors+-- | Test tweaks from BIP-0327 test vectors. testTweaks :: [Integer] testTweaks =   map@@ -58,11 +58,11 @@     , "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"     ] --- | Test message from BIP327 test vectors+-- | Test message from BIP-0327 test vectors. testMessage :: ByteString testMessage = decodeHex "F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF" --- | Valid test vector data: (key indices, nonce indices, tweak indices, is_xonly flags, signer index, expected signature, comment)+-- | Valid test vector data: (key indices, nonce indices, tweak indices, is_xonly flags, signer index, expected signature, comment). validTestVectors :: [([Int], [Int], [Int], [Bool], Int, ByteString, String)] validTestVectors =   [ ([1, 2, 0], [1, 2, 0], [0], [True], 2, decodeHex "E28A5C66E61E178C2BA19DB77B6CF9F7E2F0F56C17918CD13135E60CC848FE91", "A single x-only tweak")@@ -72,13 +72,13 @@   , ([1, 2, 0], [1, 2, 0], [0, 1, 2, 3], [True, False, True, False], 2, decodeHex "B255FDCAC27B40C7CE7848E2D3B7BF5EA0ED756DA81565AC804CCCA3E1D5D239", "Four tweaks: x-only, plain, x-only, plain. If an implementation prohibits applying plain tweaks after x-only tweaks, it can skip this test vector or return an error.")   ] --- | Error test vector data: (tweak indices, is_xonly flags, expected error message, comment)+-- | Error test vector data: (tweak indices, is_xonly flags, expected error message, comment). errorTestVectors :: [([Int], [Bool], String, String)] errorTestVectors =   [ ([4], [False], "tweaks must be less than curve order", "Tweak is invalid because it exceeds group size")   ] --- | Creates tweaks from indices and xonly flags+-- | Creates tweaks from indices and xonly flags. createTweaks :: [Int] -> [Bool] -> [Tweak] createTweaks = zipWith createTweak  where@@ -86,10 +86,10 @@     let tweakValue = testTweaks !! i      in if isXOnly then XOnlyTweak tweakValue else PlainTweak tweakValue --- | Creates test case from valid test vector data+-- | Creates test case from valid test vector data. makeValidTestCase :: Int -> ([Int], [Int], [Int], [Bool], Int, ByteString, String) -> TestTree makeValidTestCase i (keyIndices, nonceIndices, tweakIndices, isXOnly, signerIndex, expectedSig, comment) =-  testCase ("BIP327 valid test vector " <> show (i + 1) <> ": " <> comment) $ do+  testCase ("BIP-0327 valid test vector " <> show (i + 1) <> ": " <> comment) $ do     let selectedKeys = map (inputPubkeys !!) keyIndices         selectedNonces = map (inputPubNonces !!) nonceIndices         tweaks = createTweaks tweakIndices isXOnly@@ -97,46 +97,46 @@         aggNonce = fromJust $ aggNonces selectedNonces         ctx = mkSessionContext aggNonce selectedKeys tweaks testMessage -    -- Test 1: Generate signature using the sign function (with error handling)+    -- Test 1: Generate signature using the sign function (with error handling).     signResult <- try $ evaluate $ sign testSecNonce testSecKey ctx     case signResult of       Left (ErrorCall errMsg) -> do-        -- If sign fails, this indicates a potential bug in the implementation+        -- If sign fails, this indicates a potential bug in the implementation.         assertFailure ("POTENTIAL BUG: Sign function failed: " <> errMsg <> " for test: " <> comment)       Right generatedSig -> do-        -- Test 2: Verify that the generated signature is valid+        -- Test 2: Verify that the generated signature is valid.         let generatedSigVerifies = partialSigVerify generatedSig selectedNonces selectedKeys tweaks testMessage signerIndex         assertBool ("Generated signature should verify: " <> comment) generatedSigVerifies -        -- Test 3: Check if the expected signature from BIP327 test vectors verifies+        -- Test 3: Check if the expected signature from BIP-0327 test vectors verifies.         let expectedSigVerifies = partialSigVerify expectedSignature selectedNonces selectedKeys tweaks testMessage signerIndex -        -- Test 4: Compare generated signature with expected signature-        -- Note: If they don't match, it could indicate a difference in implementation or test vector interpretation+        -- Test 4: Compare generated signature with expected signature.+        -- Note: If they don't match, it could indicate a difference in implementation or test vector interpretation.         if expectedSigVerifies           then do-            -- If the expected signature verifies, we can compare it with our generated one+            -- If the expected signature verifies, we can compare it with our generated one.             if generatedSig == expectedSignature               then assertBool ("Generated signature matches expected: " <> comment) True               else do-                -- They don't match - this could be due to different nonce generation or other implementation details+                -- They don't match - this could be due to different nonce generation or other implementation details.                 -- For now, we accept this as long as both signatures verify correctly                 assertBool ("Different but valid signatures (implementation variation): " <> comment) True           else do-            -- Expected signature doesn't verify - this suggests our interpretation might be wrong+            -- Expected signature doesn't verify - this suggests our interpretation might be wrong.             -- But if our generated signature verifies, the core functionality works             assertBool ("Expected signature from test vector doesn't verify, but generated signature does: " <> comment) generatedSigVerifies --- | Creates test case from error test vector data+-- | Creates test case from error test vector data. makeErrorTestCase :: Int -> ([Int], [Bool], String, String) -> TestTree makeErrorTestCase i (tweakIndices, isXOnly, expectedError, comment) =-  testCase ("BIP327 error test vector " <> show (i + 1) <> ": " <> comment) $ do+  testCase ("BIP-0327 error test vector " <> show (i + 1) <> ": " <> comment) $ do     let selectedKeys = [inputPubkeys !! 1, inputPubkeys !! 2, head inputPubkeys] -- [1, 2, 0]         selectedNonces = [inputPubNonces !! 1, inputPubNonces !! 2, head inputPubNonces] -- [1, 2, 0]         tweaks = createTweaks tweakIndices isXOnly         aggNonce = fromJust $ aggNonces selectedNonces -    -- Test that creating a session context with invalid tweaks causes an error+    -- Test that creating a session context with invalid tweaks causes an error.     result <- try $ evaluate $ mkSessionContext aggNonce selectedKeys tweaks testMessage     case result of       Left (ErrorCall errMsg) ->@@ -145,7 +145,7 @@           (expectedError `isInfixOf` errMsg)       Right _ -> assertFailure ("Expected error but session context creation succeeded: " <> comment) --- | Test vectors from [BIP327 `tweak_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/tweak_vectors.json)+-- | Test vectors from [BIP-0327 `tweak_vectors.json`](https://github.com/bitcoin/bips/blob/master/bip-0327/vectors/tweak_vectors.json). testTweak :: TestTree testTweak =   testGroup "tweak vectors" $
test/Util.hs view
@@ -10,7 +10,7 @@ import Data.ByteString (ByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as B16-import Data.Maybe (fromJust)+import Data.Maybe (fromJust, fromMaybe) import Test.Tasty.QuickCheck (Arbitrary (..), Gen, choose, frequency, vectorOf)  {- | Parses a 'ByteString' into a 'Pub'key.@@ -51,7 +51,7 @@         ( 99         , do             scalar <- choose (0, _CURVE_Q)-            return (mul _CURVE_G scalar)+            return (fromMaybe (error "Failed to multiply scalar by generator") $ mul _CURVE_G scalar)         )       ] @@ -166,7 +166,7 @@  {- | Parses a 'ByteString' into a 'PubNonce'. -Mostly used to parse BIP327 test vectors.+Mostly used to parse BIP-0327 test vectors. -} parsePubNonce :: ByteString -> PubNonce parsePubNonce bs = PubNonce{r1 = r1', r2 = r2'}