content: -- | Some auxiliary crypto types
  module UTxO.Crypto (
      -- * Key-pairs
      RegularKeyPair(..)
    , EncKeyPair(..)
    , RedeemKeyPair(..)
    , regularKeyPair
    , encKeyPair
    , encToRegular
      -- * Abstract API
    , SomeKeyPair(..)
    , TxOwnedInput
    , ClassifiedInputs(..)
    , classifyInputs
      -- * Delegation
    , DelegatedTo(..)
    ) where
  
  import           Formatting (bprint, build, (%))
  import qualified Formatting.Buildable
  import           Universum
  
  import           Pos.Chain.Delegation (ProxySKHeavy)
  import           Pos.Chain.Txp (TxIn)
  import           Pos.Core
  import           Pos.Crypto
  
  {-------------------------------------------------------------------------------
    Keypairs
  -------------------------------------------------------------------------------}
  
  data RegularKeyPair = RegularKeyPair {
        regKpSec  :: SecretKey
      , regKpPub  :: PublicKey
      , regKpHash :: AddressHash PublicKey
      }
    deriving (Show)
  
  data EncKeyPair = EncKeyPair {
        encKpEnc  :: EncryptedSecretKey
      , encKpSec  :: SecretKey
      , encKpPub  :: PublicKey
      , encKpHash :: AddressHash PublicKey
      }
    deriving (Show)
  
  data RedeemKeyPair = RedeemKeyPair {
        redKpSec :: RedeemSecretKey
      , redKpPub :: RedeemPublicKey
      }
    deriving (Show)
  
  regularKeyPair :: SecretKey -> RegularKeyPair
  regularKeyPair regKpSec = RegularKeyPair {..}
    where
      regKpPub  = toPublic    regKpSec
      regKpHash = addressHash regKpPub
  
  encKeyPair :: EncryptedSecretKey -> EncKeyPair
  encKeyPair encKpEnc = EncKeyPair {..}
    where
      encKpSec  = encToSecret encKpEnc
      encKpPub  = encToPublic encKpEnc
      encKpHash = addressHash encKpPub
  
  encToRegular :: EncKeyPair -> RegularKeyPair
  encToRegular EncKeyPair{..} = RegularKeyPair{..}
    where
      regKpSec  = encKpSec
      regKpPub  = encKpPub
      regKpHash = encKpHash
  
  {-------------------------------------------------------------------------------
    Abstract over the various kinds of keypairs we have
  -------------------------------------------------------------------------------}
  
  data SomeKeyPair =
      KeyPairRegular RegularKeyPair
    | KeyPairEncrypted EncKeyPair
    | KeyPairRedeem RedeemKeyPair
  
  -- | An input to a transaction together with evidence that it's yours
  --
  -- (This is the singular form of 'TxOwnedInputs', which is defined in the
  -- Cardano core libraries.)
  type TxOwnedInput a = (a, TxIn)
  
  data ClassifiedInputs =
      -- | This is regular set of inputs
      InputsRegular [TxOwnedInput RegularKeyPair]
  
      -- | When redeeming from an AVVM address, we can only have a single input
    | InputsRedeem (TxOwnedInput RedeemKeyPair)
  
  -- | Classify a set of inputs
  --
  -- Maybe return an error message if the transaction contains an invalid
  -- combination of inputs.
  classifyInputs :: [TxOwnedInput SomeKeyPair] -> Either Text ClassifiedInputs
  classifyInputs = \case
      []     -> Left "No inputs"
      (i:is) -> case classifyInput i of
                  Left  i' -> go (InputsRegular [i']) is
                  Right i' -> go (InputsRedeem   i' ) is
    where
      go :: ClassifiedInputs -> [TxOwnedInput SomeKeyPair] -> Either Text ClassifiedInputs
      go acc []                     = Right $ reverseAcc acc
      go (InputsRedeem _)    (_:_)  = Left "Can only have a single redemption input"
      go (InputsRegular acc) (i:is) =
          case classifyInput i of
            Left i' -> go (InputsRegular (i':acc)) is
            Right _ -> Left "Cannot mix redemption inputs with other inputs"
  
  
      classifyInput :: TxOwnedInput SomeKeyPair
                    -> Either (TxOwnedInput RegularKeyPair)
                              (TxOwnedInput RedeemKeyPair)
      classifyInput (KeyPairRegular   kp, inp) = Left (kp, inp)
      classifyInput (KeyPairEncrypted kp, inp) = Left (encToRegular kp, inp)
      classifyInput (KeyPairRedeem    kp, inp) = Right (kp, inp)
  
      reverseAcc :: ClassifiedInputs -> ClassifiedInputs
      reverseAcc (InputsRegular inps) = InputsRegular $ reverse inps
      reverseAcc (InputsRedeem  inp)  = InputsRedeem  $ inp
  
  {-------------------------------------------------------------------------------
    Delegation
  -------------------------------------------------------------------------------}
  
  data DelegatedTo a = DelegatedTo {
        delTo  :: a
      , delPSK :: ProxySKHeavy
      }
    deriving (Show)
  
  {-------------------------------------------------------------------------------
    Pretty-printing
  -------------------------------------------------------------------------------}
  
  instance Buildable RegularKeyPair where
    build RegularKeyPair{..} = bprint
        ( "RegularKeyPair"
        % "{ sec:  " % build
        % ", pub:  " % build
        % ", hash: " % build
        % "}"
        )
        regKpSec
        regKpPub
        regKpHash
  
  instance Buildable EncKeyPair where
    build EncKeyPair{..} = bprint
        ( "EncKeyPair"
        % "{ sec:  " % build
        % ", pub:  " % build
        % ", hash: " % build
        % "}"
        )
        encKpSec
        encKpPub
        encKpHash
  
  instance Buildable RedeemKeyPair where
    build RedeemKeyPair{..} = bprint
        ( "RedeemKeyPair"
        % "{ sec: " % build
        % ", pub: " % build
        % "}"
        )
        redKpSec
        redKpPub
  
  instance Buildable a => Buildable (DelegatedTo a) where
    build DelegatedTo{..} = bprint
        ( "DelegatedTo"
        % "{ to:  " % build
        % ", psk: " % build
        % "}"
        )
        delTo
        delPSK
  