{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Shelley CLI option data types and functions for cryptographic keys.
module Cardano.CLI.Shelley.Key
  ( VerificationKeyOrFile (..)
  , readVerificationKeyOrFile
  , readVerificationKeyOrTextEnvFile

  , VerificationKeyTextOrFile (..)
  , VerificationKeyTextOrFileError (..)
  , readVerificationKeyTextOrFileAnyOf
  , renderVerificationKeyTextOrFileError

  , VerificationKeyOrHashOrFile (..)
  , readVerificationKeyOrHashOrFile
  , readVerificationKeyOrHashOrTextEnvFile

  , PaymentVerifier(..)
  , StakeVerifier(..)

  , generateKeyPair
  ) where

import           Cardano.Api

import           Control.Monad.IO.Class (MonadIO (..))
import           Data.Bifunctor (Bifunctor (..))
import qualified Data.ByteString as BS
import qualified Data.List.NonEmpty as NE
import           Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text

import           Cardano.CLI.Types


------------------------------------------------------------------------------
-- Verification key deserialisation
------------------------------------------------------------------------------

-- | Either a verification key or path to a verification key file.
data VerificationKeyOrFile keyrole
  = VerificationKeyValue !(VerificationKey keyrole)
  -- ^ A verification key.
  | VerificationKeyFilePath !VerificationKeyFile
  -- ^ A path to a verification key file.
  -- Note that this file hasn't been validated at all (whether it exists,
  -- contains a key of the correct type, etc.)

deriving instance Show (VerificationKey keyrole)
  => Show (VerificationKeyOrFile keyrole)

deriving instance Eq (VerificationKey keyrole)
  => Eq (VerificationKeyOrFile keyrole)

-- | Read a verification key or verification key file and return a
-- verification key.
--
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyOrFile
  :: ( HasTextEnvelope (VerificationKey keyrole)
     , SerialiseAsBech32 (VerificationKey keyrole)
     )
  => AsType keyrole
  -> VerificationKeyOrFile keyrole
  -> IO (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrFile :: forall keyrole.
(HasTextEnvelope (VerificationKey keyrole),
 SerialiseAsBech32 (VerificationKey keyrole)) =>
AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrFile AsType keyrole
asType VerificationKeyOrFile keyrole
verKeyOrFile =
  case VerificationKeyOrFile keyrole
verKeyOrFile of
    VerificationKeyValue VerificationKey keyrole
vk -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a b. b -> Either a b
Right VerificationKey keyrole
vk)
    VerificationKeyFilePath (VerificationKeyFile String
fp) ->
      forall a.
AsType a
-> NonEmpty (InputFormat a)
-> String
-> IO (Either (FileError InputDecodeError) a)
readKeyFile
        (forall a. AsType a -> AsType (VerificationKey a)
AsVerificationKey AsType keyrole
asType)
        (forall a. [a] -> NonEmpty a
NE.fromList [forall a. SerialiseAsBech32 a => InputFormat a
InputFormatBech32, forall a. SerialiseAsRawBytes a => InputFormat a
InputFormatHex, forall a. HasTextEnvelope a => InputFormat a
InputFormatTextEnvelope])
        String
fp

-- | Read a verification key or verification key file and return a
-- verification key.
--
-- If a filepath is provided, it will be interpreted as a text envelope
-- formatted file.
readVerificationKeyOrTextEnvFile
  :: HasTextEnvelope (VerificationKey keyrole)
  => AsType keyrole
  -> VerificationKeyOrFile keyrole
  -> IO (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrTextEnvFile :: forall keyrole.
HasTextEnvelope (VerificationKey keyrole) =>
AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrTextEnvFile AsType keyrole
asType VerificationKeyOrFile keyrole
verKeyOrFile =
  case VerificationKeyOrFile keyrole
verKeyOrFile of
    VerificationKeyValue VerificationKey keyrole
vk -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a b. b -> Either a b
Right VerificationKey keyrole
vk)
    VerificationKeyFilePath (VerificationKeyFile String
fp) ->
      forall a.
HasTextEnvelope a =>
AsType a -> String -> IO (Either (FileError InputDecodeError) a)
readKeyFileTextEnvelope (forall a. AsType a -> AsType (VerificationKey a)
AsVerificationKey AsType keyrole
asType) String
fp

data PaymentVerifier
  = PaymentVerifierKey VerificationKeyTextOrFile
  | PaymentVerifierScriptFile ScriptFile
  deriving (PaymentVerifier -> PaymentVerifier -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PaymentVerifier -> PaymentVerifier -> Bool
$c/= :: PaymentVerifier -> PaymentVerifier -> Bool
== :: PaymentVerifier -> PaymentVerifier -> Bool
$c== :: PaymentVerifier -> PaymentVerifier -> Bool
Eq, Int -> PaymentVerifier -> ShowS
[PaymentVerifier] -> ShowS
PaymentVerifier -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PaymentVerifier] -> ShowS
$cshowList :: [PaymentVerifier] -> ShowS
show :: PaymentVerifier -> String
$cshow :: PaymentVerifier -> String
showsPrec :: Int -> PaymentVerifier -> ShowS
$cshowsPrec :: Int -> PaymentVerifier -> ShowS
Show)

data StakeVerifier
  = StakeVerifierKey (VerificationKeyOrFile StakeKey)
  | StakeVerifierScriptFile ScriptFile
  | StakeVerifierAddress StakeAddress
  deriving (StakeVerifier -> StakeVerifier -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: StakeVerifier -> StakeVerifier -> Bool
$c/= :: StakeVerifier -> StakeVerifier -> Bool
== :: StakeVerifier -> StakeVerifier -> Bool
$c== :: StakeVerifier -> StakeVerifier -> Bool
Eq, Int -> StakeVerifier -> ShowS
[StakeVerifier] -> ShowS
StakeVerifier -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [StakeVerifier] -> ShowS
$cshowList :: [StakeVerifier] -> ShowS
show :: StakeVerifier -> String
$cshow :: StakeVerifier -> String
showsPrec :: Int -> StakeVerifier -> ShowS
$cshowsPrec :: Int -> StakeVerifier -> ShowS
Show)

-- | Either an unvalidated text representation of a verification key or a path
-- to a verification key file.
data VerificationKeyTextOrFile
  = VktofVerificationKeyText !Text
  | VktofVerificationKeyFile !VerificationKeyFile
  deriving (VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
$c/= :: VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
== :: VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
$c== :: VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
Eq, Int -> VerificationKeyTextOrFile -> ShowS
[VerificationKeyTextOrFile] -> ShowS
VerificationKeyTextOrFile -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [VerificationKeyTextOrFile] -> ShowS
$cshowList :: [VerificationKeyTextOrFile] -> ShowS
show :: VerificationKeyTextOrFile -> String
$cshow :: VerificationKeyTextOrFile -> String
showsPrec :: Int -> VerificationKeyTextOrFile -> ShowS
$cshowsPrec :: Int -> VerificationKeyTextOrFile -> ShowS
Show)

-- | An error in deserialising a 'VerificationKeyTextOrFile' to a
-- 'VerificationKey'.
data VerificationKeyTextOrFileError
  = VerificationKeyTextError !InputDecodeError
  | VerificationKeyFileError !(FileError InputDecodeError)
  deriving Int -> VerificationKeyTextOrFileError -> ShowS
[VerificationKeyTextOrFileError] -> ShowS
VerificationKeyTextOrFileError -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [VerificationKeyTextOrFileError] -> ShowS
$cshowList :: [VerificationKeyTextOrFileError] -> ShowS
show :: VerificationKeyTextOrFileError -> String
$cshow :: VerificationKeyTextOrFileError -> String
showsPrec :: Int -> VerificationKeyTextOrFileError -> ShowS
$cshowsPrec :: Int -> VerificationKeyTextOrFileError -> ShowS
Show

-- | Render an error message for a 'VerificationKeyTextOrFileError'.
renderVerificationKeyTextOrFileError :: VerificationKeyTextOrFileError -> Text
renderVerificationKeyTextOrFileError :: VerificationKeyTextOrFileError -> Text
renderVerificationKeyTextOrFileError VerificationKeyTextOrFileError
vkTextOrFileErr =
  case VerificationKeyTextOrFileError
vkTextOrFileErr of
    VerificationKeyTextError InputDecodeError
err -> InputDecodeError -> Text
renderInputDecodeError InputDecodeError
err
    VerificationKeyFileError FileError InputDecodeError
err -> String -> Text
Text.pack (forall e. Error e => e -> String
displayError FileError InputDecodeError
err)

-- | Deserialise a verification key from text or a verification key file.
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyTextOrFileAnyOf
  :: VerificationKeyTextOrFile
  -> IO (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
readVerificationKeyTextOrFileAnyOf :: VerificationKeyTextOrFile
-> IO
     (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
readVerificationKeyTextOrFileAnyOf VerificationKeyTextOrFile
verKeyTextOrFile =
  case VerificationKeyTextOrFile
verKeyTextOrFile of
    VktofVerificationKeyText Text
vkText ->
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first InputDecodeError -> VerificationKeyTextOrFileError
VerificationKeyTextError forall a b. (a -> b) -> a -> b
$
        ByteString -> Either InputDecodeError SomeAddressVerificationKey
deserialiseAnyVerificationKey (Text -> ByteString
Text.encodeUtf8 Text
vkText)
    VktofVerificationKeyFile (VerificationKeyFile String
fp) -> do
      ByteString
vkBs <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ String -> IO ByteString
BS.readFile String
fp
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first InputDecodeError -> VerificationKeyTextOrFileError
VerificationKeyTextError forall a b. (a -> b) -> a -> b
$
        ByteString -> Either InputDecodeError SomeAddressVerificationKey
deserialiseAnyVerificationKey ByteString
vkBs


-- | Verification key, verification key hash, or path to a verification key
-- file.
data VerificationKeyOrHashOrFile keyrole
  = VerificationKeyOrFile !(VerificationKeyOrFile keyrole)
  -- ^ Either a verification key or path to a verification key file.
  | VerificationKeyHash !(Hash keyrole)
  -- ^ A verification key hash.

deriving instance (Show (VerificationKeyOrFile keyrole), Show (Hash keyrole))
  => Show (VerificationKeyOrHashOrFile keyrole)

deriving instance (Eq (VerificationKeyOrFile keyrole), Eq (Hash keyrole))
  => Eq (VerificationKeyOrHashOrFile keyrole)

-- | Read a verification key or verification key hash or verification key file
-- and return a verification key hash.
--
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyOrHashOrFile
  :: (Key keyrole, SerialiseAsBech32 (VerificationKey keyrole))
  => AsType keyrole
  -> VerificationKeyOrHashOrFile keyrole
  -> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrFile :: forall keyrole.
(Key keyrole, SerialiseAsBech32 (VerificationKey keyrole)) =>
AsType keyrole
-> VerificationKeyOrHashOrFile keyrole
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrFile AsType keyrole
asType VerificationKeyOrHashOrFile keyrole
verKeyOrHashOrFile =
  case VerificationKeyOrHashOrFile keyrole
verKeyOrHashOrFile of
    VerificationKeyOrFile VerificationKeyOrFile keyrole
vkOrFile -> do
      Either (FileError InputDecodeError) (VerificationKey keyrole)
eitherVk <- forall keyrole.
(HasTextEnvelope (VerificationKey keyrole),
 SerialiseAsBech32 (VerificationKey keyrole)) =>
AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrFile AsType keyrole
asType VerificationKeyOrFile keyrole
vkOrFile
      forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either (FileError InputDecodeError) (VerificationKey keyrole)
eitherVk)
    VerificationKeyHash Hash keyrole
vkHash -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a b. b -> Either a b
Right Hash keyrole
vkHash)

-- | Read a verification key or verification key hash or verification key file
-- and return a verification key hash.
--
-- If a filepath is provided, it will be interpreted as a text envelope
-- formatted file.
readVerificationKeyOrHashOrTextEnvFile
  :: Key keyrole
  => AsType keyrole
  -> VerificationKeyOrHashOrFile keyrole
  -> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrTextEnvFile :: forall keyrole.
Key keyrole =>
AsType keyrole
-> VerificationKeyOrHashOrFile keyrole
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrTextEnvFile AsType keyrole
asType VerificationKeyOrHashOrFile keyrole
verKeyOrHashOrFile =
  case VerificationKeyOrHashOrFile keyrole
verKeyOrHashOrFile of
    VerificationKeyOrFile VerificationKeyOrFile keyrole
vkOrFile -> do
      Either (FileError InputDecodeError) (VerificationKey keyrole)
eitherVk <- forall keyrole.
HasTextEnvelope (VerificationKey keyrole) =>
AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrTextEnvFile AsType keyrole
asType VerificationKeyOrFile keyrole
vkOrFile
      forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either (FileError InputDecodeError) (VerificationKey keyrole)
eitherVk)
    VerificationKeyHash Hash keyrole
vkHash -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a b. b -> Either a b
Right Hash keyrole
vkHash)

generateKeyPair :: Key keyrole => AsType keyrole -> IO (VerificationKey keyrole, SigningKey keyrole)
generateKeyPair :: forall keyrole.
Key keyrole =>
AsType keyrole -> IO (VerificationKey keyrole, SigningKey keyrole)
generateKeyPair AsType keyrole
asType = do
  SigningKey keyrole
skey <- forall keyrole.
Key keyrole =>
AsType keyrole -> IO (SigningKey keyrole)
generateSigningKey AsType keyrole
asType
  forall (m :: * -> *) a. Monad m => a -> m a
return (forall keyrole.
Key keyrole =>
SigningKey keyrole -> VerificationKey keyrole
getVerificationKey SigningKey keyrole
skey, SigningKey keyrole
skey)