module Hydra.Node.UtilSpec where

import Hydra.Prelude
import Test.Hydra.Prelude

import Data.List (isInfixOf)
import Hydra.Cardano.Api (
  CardanoSigningKey (..),
  getCardanoPaymentVerificationKey,
  verificationKeyHash,
 )
import Hydra.Node.Util (KeyFileError (..), readKeyPair, readSigningKey, readVerificationKey)
import Hydra.Tx.Secret (withSecret)

spec :: Spec
spec :: Spec
spec = do
  [Char] -> Spec -> Spec
forall a. HasCallStack => [Char] -> SpecWith a -> SpecWith a
describe [Char]
"readKeyPair" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"loads an extended PaymentExtendedKey and returns correct vk" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      (VerificationKey PaymentKey
vk, Secret CardanoSigningKey
csk) <- [Char] -> IO (VerificationKey PaymentKey, Secret CardanoSigningKey)
readKeyPair [Char]
"test/fixtures/payment-extended.sk"
      VerificationKey PaymentKey
extVk <- [Char] -> IO (VerificationKey PaymentKey)
readVerificationKey [Char]
"test/fixtures/payment-extended.vk"
      VerificationKey PaymentKey -> Hash PaymentKey
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash VerificationKey PaymentKey
vk Hash PaymentKey -> Hash PaymentKey -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` VerificationKey PaymentKey -> Hash PaymentKey
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash VerificationKey PaymentKey
extVk
      Secret CardanoSigningKey -> (CardanoSigningKey -> IO ()) -> IO ()
forall a r. Secret a -> (a -> r) -> r
withSecret Secret CardanoSigningKey
csk ((CardanoSigningKey -> IO ()) -> IO ())
-> (CardanoSigningKey -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \case
        CardanoExtendedSigningKey{} -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
        CardanoSigningKey{} -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure [Char]
"Expected extended signing key"

    [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"loads a normal PaymentKey and returns correct vk" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      (VerificationKey PaymentKey
vk, Secret CardanoSigningKey
csk) <- [Char] -> IO (VerificationKey PaymentKey, Secret CardanoSigningKey)
readKeyPair [Char]
"test/fixtures/payment-normal.sk"
      -- The vk derived from reading the key pair should match the vk derived from the signing key
      VerificationKey PaymentKey -> Hash PaymentKey
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash VerificationKey PaymentKey
vk Hash PaymentKey -> Hash PaymentKey -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` Secret CardanoSigningKey
-> (CardanoSigningKey -> Hash PaymentKey) -> Hash PaymentKey
forall a r. Secret a -> (a -> r) -> r
withSecret Secret CardanoSigningKey
csk (VerificationKey PaymentKey -> Hash PaymentKey
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash (VerificationKey PaymentKey -> Hash PaymentKey)
-> (CardanoSigningKey -> VerificationKey PaymentKey)
-> CardanoSigningKey
-> Hash PaymentKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CardanoSigningKey -> VerificationKey PaymentKey
getCardanoPaymentVerificationKey)
      Secret CardanoSigningKey -> (CardanoSigningKey -> IO ()) -> IO ()
forall a r. Secret a -> (a -> r) -> r
withSecret Secret CardanoSigningKey
csk ((CardanoSigningKey -> IO ()) -> IO ())
-> (CardanoSigningKey -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \case
        CardanoSigningKey{} -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
        CardanoExtendedSigningKey{} -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure [Char]
"Expected normal signing key"

  [Char] -> Spec -> Spec
forall a. HasCallStack => [Char] -> SpecWith a -> SpecWith a
describe [Char]
"readSigningKey" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"loads an extended signing key natively and produces matching verification key" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      CardanoSigningKey
csk <- [Char] -> IO CardanoSigningKey
readSigningKey [Char]
"test/fixtures/payment-extended.sk"
      VerificationKey PaymentKey
extVk <- [Char] -> IO (VerificationKey PaymentKey)
readVerificationKey [Char]
"test/fixtures/payment-extended.vk"
      VerificationKey PaymentKey -> Hash PaymentKey
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash (CardanoSigningKey -> VerificationKey PaymentKey
getCardanoPaymentVerificationKey CardanoSigningKey
csk) Hash PaymentKey -> Hash PaymentKey -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` VerificationKey PaymentKey -> Hash PaymentKey
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash VerificationKey PaymentKey
extVk

    [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"loads a normal signing key" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      CardanoSigningKey
csk <- [Char] -> IO CardanoSigningKey
readSigningKey [Char]
"test/fixtures/payment-normal.sk"
      case CardanoSigningKey
csk of
        CardanoSigningKey{} -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
        CardanoExtendedSigningKey{} -> HasCallStack => [Char] -> IO ()
[Char] -> IO ()
expectationFailure [Char]
"Expected normal signing key"

    [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"fails on a wrong key type mentioning both accepted formats" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$
      [Char] -> IO CardanoSigningKey
readSigningKey [Char]
"test/fixtures/payment-normal.vk"
        IO CardanoSigningKey -> Selector KeyFileError -> IO ()
forall e a.
(HasCallStack, Exception e) =>
IO a -> Selector e -> IO ()
`shouldThrow` \(KeyFileError FileError TextEnvelopeError
e) ->
          ([Char] -> Bool) -> [[Char]] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all
            ([Char] -> [Char] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` FileError TextEnvelopeError -> [Char]
forall b a. (Show a, IsString b) => a -> b
show FileError TextEnvelopeError
e)
            [ [Char]
"PaymentSigningKeyShelley_ed25519"
            , [Char]
"PaymentExtendedSigningKeyShelley_ed25519_bip32"
            ]

  [Char] -> Spec -> Spec
forall a. HasCallStack => [Char] -> SpecWith a -> SpecWith a
describe [Char]
"readVerificationKey" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"loads a normal verification key" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      VerificationKey PaymentKey
vk <- [Char] -> IO (VerificationKey PaymentKey)
readVerificationKey [Char]
"test/fixtures/payment-normal.vk"
      VerificationKey PaymentKey
vk VerificationKey PaymentKey
-> (VerificationKey PaymentKey -> Bool) -> IO ()
forall a. (HasCallStack, Show a) => a -> (a -> Bool) -> IO ()
`shouldSatisfy` Bool -> VerificationKey PaymentKey -> Bool
forall a b. a -> b -> a
const Bool
True

    [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"loads an extended verification key and converts it" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$ do
      VerificationKey PaymentKey
extVk <- [Char] -> IO (VerificationKey PaymentKey)
readVerificationKey [Char]
"test/fixtures/payment-extended.vk"
      VerificationKey PaymentKey
normalVk <- [Char] -> IO (VerificationKey PaymentKey)
readVerificationKey [Char]
"test/fixtures/payment-normal.vk"
      VerificationKey PaymentKey -> Hash PaymentKey
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash VerificationKey PaymentKey
extVk Hash PaymentKey -> Hash PaymentKey -> IO ()
forall a. (HasCallStack, Show a, Eq a) => a -> a -> IO ()
`shouldBe` VerificationKey PaymentKey -> Hash PaymentKey
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash VerificationKey PaymentKey
normalVk

    [Char] -> IO () -> SpecWith (Arg (IO ()))
forall a.
(HasCallStack, Example a) =>
[Char] -> a -> SpecWith (Arg a)
it [Char]
"fails on a wrong key type mentioning both accepted formats" (IO () -> SpecWith (Arg (IO ())))
-> IO () -> SpecWith (Arg (IO ()))
forall a b. (a -> b) -> a -> b
$
      [Char] -> IO (VerificationKey PaymentKey)
readVerificationKey [Char]
"test/fixtures/payment-normal.sk"
        IO (VerificationKey PaymentKey) -> Selector KeyFileError -> IO ()
forall e a.
(HasCallStack, Exception e) =>
IO a -> Selector e -> IO ()
`shouldThrow` \(KeyFileError FileError TextEnvelopeError
e) ->
          ([Char] -> Bool) -> [[Char]] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all
            ([Char] -> [Char] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` FileError TextEnvelopeError -> [Char]
forall b a. (Show a, IsString b) => a -> b
show FileError TextEnvelopeError
e)
            [ [Char]
"PaymentVerificationKeyShelley_ed25519"
            , [Char]
"PaymentExtendedVerificationKeyShelley_ed25519_bip32"
            ]