packages feed

musig2-0.1.0: test/ApplyTweaks.hs

{-# LANGUAGE OverloadedStrings #-}

module ApplyTweaks (testApplyTweaks) where

import Crypto.Curve.Secp256k1 (Pub, parse_point)
import Crypto.Curve.Secp256k1.MuSig2 (Tweak (..), aggregatedPubkey, applyTweak, mkKeyAggContext)
import Crypto.Curve.Secp256k1.MuSig2.Internal (bytesToInteger, hashTag)
import Data.ByteString (ByteString)
import Data.Maybe (fromJust)
import Test.Tasty
import Test.Tasty.HUnit
import Util (decodeHex, extractXOnly, parsePoint)

-- | Computes taproot tweak from pubkey and merkle root.
computeTaprootTweak :: Pub -> ByteString -> Integer
computeTaprootTweak pubkey merkleRoot =
  let pubkeyXOnly = extractXOnly pubkey
      tweakHash = hashTag "TapTweak" (pubkeyXOnly <> merkleRoot)
   in bytesToInteger tweakHash

-- | Test pubkeys from the Rust's `musig2` crate `test_aggregation_context_tweaks`.
testPubkeys :: [Pub]
testPubkeys =
  map
    parsePoint
    [ "03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
    , "02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"
    , "02DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
    ]

-- | Test case: Sequence of tweaks matching `test_aggregation_context_tweaks` from Rust's `musig2` crate.
testTweakSequence :: TestTree
testTweakSequence =
  testCase "Tweak sequence" $ do
    let keyAggCtx1 = mkKeyAggContext testPubkeys Nothing

    -- Apply first X-only tweak
    let tweak1 = XOnlyTweak 0xE8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB
        keyAggCtx2 = applyTweak keyAggCtx1 tweak1

    -- Apply second X-only tweak
    let tweak2 = XOnlyTweak 0xAE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455
        keyAggCtx3 = applyTweak keyAggCtx2 tweak2

    -- Apply third plain tweak
    let tweak3 = PlainTweak 0xF52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0
        keyAggCtx4 = applyTweak keyAggCtx3 tweak3

    -- Apply fourth plain tweak
    let tweak4 = PlainTweak 0x1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D
        finalKeyAggCtx = applyTweak keyAggCtx4 tweak4
        finalAggPk = aggregatedPubkey finalKeyAggCtx
        expected = fromJust $ parse_point $ decodeHex "0269434B39A026A4AAC9E6C1AEBDD3993FFA581C8F7F21B6FAAE15608057F5CE85"

    expected @=? finalAggPk

-- | Test case: Taproot tweak with merkle root matching `test_aggregation_context_tweaks` from Rust's `musig2` crate.
testTaprootTweak :: TestTree
testTaprootTweak =
  testCase "Taproot tweak with merkle root" $ do
    let keyAggCtx = mkKeyAggContext testPubkeys Nothing
        originalPk = aggregatedPubkey keyAggCtx
        merkleRootBytes = decodeHex "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        -- Compute proper taproot tweak hash
        taprootTweakValue = computeTaprootTweak originalPk merkleRootBytes
        taprootTweak = XOnlyTweak taprootTweakValue
        tweakedCtx = applyTweak keyAggCtx taprootTweak
        tweakedPk = aggregatedPubkey tweakedCtx
        expected = fromJust $ parse_point $ decodeHex "024650cca5e389f62e960f66ca0400927a7727fc6e84b9c38a1fd9a80271377ceb"

    expected @=? tweakedPk

-- | Test vectors the Rust's `musig2` crate `test_aggregation_context_tweaks`.
testApplyTweaks :: TestTree
testApplyTweaks =
  testGroup
    "applying tweaks"
    [ testTweakSequence
    , testTaprootTweak
    ]