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 +5/−1
- lib/Crypto/Curve/Secp256k1/MuSig2.hs +48/−25
- lib/Crypto/Curve/Secp256k1/MuSig2/Internal.hs +6/−4
- musig2.cabal +3/−3
- test/AggNonces.hs +3/−3
- test/AggPartials.hs +7/−7
- test/AggPartialsProperty.hs +8/−8
- test/AggPubkeys.hs +5/−5
- test/NonceGen.hs +3/−3
- test/NonceGenProperty.hs +2/−1
- test/ParityPub.hs +1/−1
- test/SignVerify.hs +14/−14
- test/SignVerifyProperty.hs +5/−5
- test/SignVerifyTweakProperty.hs +6/−6
- test/SortPubkeys.hs +2/−2
- test/Tweak.hs +24/−24
- test/Util.hs +3/−3
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'}