musig2-0.3.0: test/Generators.hs
{- | QuickCheck generators.
Plain 'Gen' values used with @forAll@/@forAllShow@, so the test suite defines
no orphan 'Arbitrary' instances for library types. Secrets have no 'Show'
instance; properties show them through 'secKeyToBytes'.
Scalars are drawn from the full range \([0, n)\) (not QuickCheck's small
integers), with extra weight on the edge cases \(0\), \(1\) and \(n - 1\).
-}
module Generators (
genScalar,
genNonZeroScalar,
genSecKey,
genPubKey,
genTweak,
TweakOp (..),
genTweakOp,
applyTweakOp,
genBytes,
genMessage,
genRand,
genPubNonce,
genAggNonce,
genPartialSignature,
showSecKey,
) where
import Crypto.Curve.Secp256k1 (Projective, _CURVE_ZERO)
import Crypto.Curve.Secp256k1.MuSig2 (
AggNonce,
KeyAggContext,
KeyAggError,
PartialSignature,
PubKey,
PubNonce,
SecKey,
Tweak,
applyPlainTweak,
applyTaprootTweak,
applyXOnlyTweak,
secKeyPubKey,
secKeyToBytes,
)
import Crypto.Curve.Secp256k1.MuSig2.Internal.Point (mulGPublic)
import Crypto.Curve.Secp256k1.MuSig2.Internal.Scalar (curveOrder)
import Crypto.Curve.Secp256k1.MuSig2.Internal.Types (mkAggNonce, mkPubNonce)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Test.Tasty.QuickCheck (Gen, arbitrary, choose, frequency, oneof, vectorOf)
import Util (hexOf, psigFromInt, secKeyFromInt, tweakFromInt)
-- | A scalar in \([0, n)\), uniform with extra weight on the edges.
genScalar :: Gen Integer
genScalar = frequency [(1, pure 0), (1, pure 1), (1, pure (curveOrder - 1)), (7, choose (0, curveOrder - 1))]
-- | A scalar in \([1, n)\).
genNonZeroScalar :: Gen Integer
genNonZeroScalar = frequency [(1, pure 1), (1, pure (curveOrder - 1)), (8, choose (1, curveOrder - 1))]
genSecKey :: Gen SecKey
genSecKey = secKeyFromInt <$> genNonZeroScalar
genPubKey :: Gen PubKey
genPubKey = secKeyPubKey <$> genSecKey
genTweak :: Gen Tweak
genTweak = tweakFromInt <$> genScalar
-- | A tweak to apply to a key aggregation context.
data TweakOp
= Plain Tweak
| XOnly Tweak
| -- | BIP-341 Taproot tweak with an optional 32-byte merkle root.
Taproot (Maybe ByteString)
deriving (Show)
genTweakOp :: Gen TweakOp
genTweakOp = oneof [Plain <$> genTweak, XOnly <$> genTweak, Taproot <$> oneof [pure Nothing, Just <$> genBytes 32]]
applyTweakOp :: KeyAggContext -> TweakOp -> Either KeyAggError KeyAggContext
applyTweakOp ctx op = case op of
Plain t -> applyPlainTweak t ctx
XOnly t -> applyXOnlyTweak t ctx
Taproot root -> applyTaprootTweak root ctx
genBytes :: Int -> Gen ByteString
genBytes n = BS.pack <$> vectorOf n arbitrary
-- | Messages: empty, 32 bytes (the usual sighash), or any length up to 100.
genMessage :: Gen ByteString
genMessage = oneof [pure BS.empty, genBytes 32, choose (0, 100) >>= genBytes]
-- | 32 random bytes.
genRand :: Gen ByteString
genRand = genBytes 32
pointOf :: Integer -> Gen Projective
pointOf k = pure (mulGPublic k)
genPubNonce :: Gen PubNonce
genPubNonce = mkPubNonce <$> (genNonZeroScalar >>= pointOf) <*> (genNonZeroScalar >>= pointOf)
-- | An aggregate nonce; either half may be infinity.
genAggNonce :: Gen AggNonce
genAggNonce = mkAggNonce <$> half <*> half
where
half = frequency [(1, pure _CURVE_ZERO), (4, genNonZeroScalar >>= pointOf)]
genPartialSignature :: Gen PartialSignature
genPartialSignature = psigFromInt <$> genScalar
-- | Shows a secret key's bytes, for counterexamples.
showSecKey :: SecKey -> String
showSecKey = hexOf . secKeyToBytes