packages feed

solana-haskell-sdk-1.2.0.0: test/Test/NativePrograms/Secp256k1.hs

module Test.NativePrograms.Secp256k1 (tests) where

import Control.Exception (ErrorCall, evaluate, try)
import Data.Binary (decode, encode)
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as BL
import Network.Solana.Core.Instruction (iAccounts, iData, instrData)
import Network.Solana.NativePrograms.Secp256k1 qualified as Secp
import Test.Fixtures
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck

genOffsets :: Gen Secp.SecpSignatureOffsets
genOffsets =
  Secp.SecpSignatureOffsets
    <$> arbitrary
    <*> arbitrary
    <*> arbitrary
    <*> arbitrary
    <*> arbitrary
    <*> arbitrary
    <*> arbitrary

tests :: TestTree
tests =
  withResource (loadFixtures "test/fixtures/secp256k1.json") (const (pure ())) $ \getFixtures ->
    testGroup
      "Secp256k1 instruction data (golden + properties)"
      [ testCase "newSecp256k1Instruction reconstructs the full instruction data" $ do
          fs <- getFixtures
          let ethAddress = requireFixture "secp-eth-address" fs
              signature = requireFixture "secp-signature" fs
              recoveryId = BS.head (requireFixture "secp-recovery-id" fs)
              message = requireFixture "secp-message" fs
              ix = Secp.newSecp256k1Instruction ethAddress signature recoveryId message
          instrData (iData ix) @?= requireFixture "secp-full-instruction-data" fs,
        testCase "offsets golden: decode from the full instruction data" $ do
          fs <- getFixtures
          let full = requireFixture "secp-full-instruction-data" fs
              offsets = decode (BL.fromStrict (BS.take 11 (BS.drop 1 full))) :: Secp.SecpSignatureOffsets
          offsets
            @?= Secp.SecpSignatureOffsets
              { Secp.ssoSignatureOffset = 32,
                Secp.ssoSignatureInstructionIndex = 0,
                Secp.ssoEthAddressOffset = 12,
                Secp.ssoEthAddressInstructionIndex = 0,
                Secp.ssoMessageDataOffset = 97,
                Secp.ssoMessageDataSize = 10,
                Secp.ssoMessageInstructionIndex = 0
              },
        testProperty "SecpSignatureOffsets Binary round-trip" $
          forAll genOffsets $ \o -> decode (encode o) === o,
        testCase "newSecp256k1Instruction rejects a 19-byte ethAddress" $ do
          result <-
            try
              ( evaluate
                  (Secp.newSecp256k1Instruction (BS.replicate 19 0) (BS.replicate 64 0) 0 (BS.replicate 10 0))
              )
          case result of
            Left (_ :: ErrorCall) -> pure ()
            Right _ -> assertFailure "expected error for a 19-byte ethAddress",
        testCase "newSecp256k1Instruction rejects a 63-byte signature" $ do
          result <-
            try
              ( evaluate
                  (Secp.newSecp256k1Instruction (BS.replicate 20 0) (BS.replicate 63 0) 0 (BS.replicate 10 0))
              )
          case result of
            Left (_ :: ErrorCall) -> pure ()
            Right _ -> assertFailure "expected error for a 63-byte signature",
        testCase "newSecp256k1Instruction takes no accounts" $
          iAccounts (Secp.newSecp256k1Instruction (BS.replicate 20 0) (BS.replicate 64 0) 0 (BS.replicate 10 0))
            @?= []
      ]