content: {-# LANGUAGE AllowAmbiguousTypes #-}
  {-# LANGUAGE BinaryLiterals #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE DeriveFunctor #-}
  {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE DerivingStrategies #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE StandaloneDeriving #-}
  {-# LANGUAGE TypeFamilies #-}
  
  {-# OPTIONS_HADDOCK prune #-}
  
  -- |
  -- Copyright: 2020 Input Output (Hong Kong) Ltd., 2021-2022 Input Output Global Inc. (IOG), 2023-2025 Intersect
  -- License: Apache-2.0
  
  module Cardano.Address.Style.Shared
      ( -- $overview
  
        -- * Shared
        Shared
      , getKey
      , liftXPrv
      , liftXPub
      , sharedWalletId
  
        -- * Key Derivation
        -- $keyDerivation
      , genMasterKeyFromXPrv
      , genMasterKeyFromMnemonic
      , deriveAccountPrivateKey
      , deriveAddressPrivateKey
      , deriveAddressPublicKey
      , deriveDelegationPrivateKey
      , deriveDelegationPublicKey
      , hashKey
  
      ) where
  
  import Prelude
  
  import Cardano.Address.Derivation
      ( Depth (..)
      , DerivationType (..)
      , Index (..)
      , XPrv
      , XPub
      , hashCredential
      , hashWalletId
      , xpubPublicKey
      )
  import Cardano.Address.KeyHash
      ( KeyHash (..), KeyRole )
  import Cardano.Address.Script
      ( Cosigner, Script )
  import Cardano.Address.Script.Parser
      ( scriptToText )
  import Cardano.Address.Style.Shelley
      ( Role (..)
      , deriveAccountPrivateKeyShelley
      , deriveAddressPrivateKeyShelley
      , deriveAddressPublicKeyShelley
      , genMasterKeyFromMnemonicShelley
      )
  import Cardano.Mnemonic
      ( SomeMnemonic )
  import Control.DeepSeq
      ( NFData )
  import Data.ByteArray
      ( ScrubbedBytes )
  import Data.ByteString
      ( ByteString )
  import Data.Coerce
      ( coerce )
  import Data.Word
      ( Word32 )
  import GHC.Generics
      ( Generic )
  
  import qualified Cardano.Address.Derivation as Internal
  import qualified Data.ByteString as BS
  import qualified Data.Text.Encoding as T
  
  
  -- $overview
  --
  -- This module provides an implementation of:
  --
  -- - 'Cardano.Address.Derivation.GenMasterKey': for generating Shared master keys from mnemonic sentences
  -- - 'Cardano.Address.Derivation.HardDerivation': for hierarchical hard derivation of parent to child keys
  -- - 'Cardano.Address.Derivation.SoftDerivation': for hierarchical soft derivation of parent to child keys
  --
  -- - 'paymentAddress': for constructing payment addresses from a address public key or a script
  -- - 'delegationAddress': for constructing delegation addresses from payment credential (public key or script) and stake credential (public key or script)
  -- - 'pointerAddress': for constructing delegation addresses from payment credential (public key or script) and chain pointer
  -- - 'stakeAddress': for constructing reward accounts from stake credential (public key or script)
  
  -- | A cryptographic key for sequential-scheme address derivation, with
  -- phantom-types to disambiguate key types. The derivation is mostly like Shelley, except the used purpose index
  -- (here 1854H rather than Shelley's 1852H)
  --
  -- @
  -- let rootPrivateKey = Shared 'RootK XPrv
  -- let accountPubKey  = Shared 'AccountK XPub
  -- let addressPubKey  = Shared 'PaymentK XPub
  -- @
  --
  -- @since 3.4.0
  newtype Shared (depth :: Depth) key = Shared
      { getKey :: key
          -- ^ Extract the raw 'XPrv' or 'XPub' wrapped by this type.
          --
          -- @since 3.4.0
      }
      deriving stock (Generic, Show, Eq)
  
  deriving instance (Functor (Shared depth))
  instance (NFData key) => NFData (Shared depth key)
  
  --
  -- Key Derivation
  --
  -- $keyDerivation
  --
  -- ==== Generating a root key from 'SomeMnemonic'
  --
  -- @
  -- >>> :set -XOverloadedStrings
  -- >>> :set -XTypeApplications
  -- >>> :set -XDataKinds
  -- >>> import Cardano.Mnemonic ( mkSomeMnemonic )
  -- @
  --
  -- @
  -- >>> let (Right mw) = mkSomeMnemonic @'[15] ["network","empty","cause","mean","expire","private","finger","accident","session","problem","absurd","banner","stage","void","what"]
  -- >>> let sndFactor = mempty -- Or alternatively, a second factor mnemonic transformed to bytes via someMnemonicToBytes
  -- >>> let rootK = genMasterKeyFromMnemonic mw sndFactor :: Shared 'RootK XPrv
  -- @
  --
  -- ==== Deriving child keys
  --
  -- Let's consider the following 3rd, 4th and 5th derivation paths @0'\/0\/14@
  --
  -- @
  -- >>> let Just accIx = indexFromWord32 0x80000000
  -- >>> let acctK = deriveAccountPrivateKey rootK accIx
  -- >>>
  -- >>> let Just addIx = indexFromWord32 0x00000014
  -- >>> let addrK = deriveAddressPrivateKey acctK UTxOExternal addIx
  --
  -- >>> let stakeK = deriveDelegationPrivateKey acctK
  -- @
  instance Internal.GenMasterKey Shared where
      type SecondFactor Shared = ScrubbedBytes
  
      genMasterKeyFromXPrv = liftXPrv
      genMasterKeyFromMnemonic fstFactor sndFactor =
          Shared $ genMasterKeyFromMnemonicShelley fstFactor sndFactor
  
  instance Internal.HardDerivation Shared where
      type AccountIndexDerivationType Shared = 'Hardened
      type AddressIndexDerivationType Shared = 'Soft
      type WithRole Shared = Role
  
      deriveAccountPrivateKey (Shared rootXPrv) accIx =
          Shared $ deriveAccountPrivateKeyShelley rootXPrv accIx purposeIndex
  
      deriveAddressPrivateKey (Shared accXPrv) keyRole addrIx =
          Shared $ deriveAddressPrivateKeyShelley accXPrv keyRole addrIx
  
  instance Internal.SoftDerivation Shared where
      deriveAddressPublicKey (Shared accXPub) keyRole addrIx =
          Shared $ deriveAddressPublicKeyShelley accXPub keyRole addrIx
  
  -- | Generate a root key from a corresponding mnemonic.
  --
  -- @since 3.4.0
  genMasterKeyFromMnemonic
      :: SomeMnemonic
          -- ^ Some valid mnemonic sentence.
      -> ScrubbedBytes
          -- ^ An optional second-factor passphrase (or 'mempty')
      -> Shared 'RootK XPrv
  genMasterKeyFromMnemonic = Internal.genMasterKeyFromMnemonic
  
  -- | Generate a root key from a corresponding root 'XPrv'
  --
  -- @since 3.4.0
  genMasterKeyFromXPrv :: XPrv -> Shared 'RootK XPrv
  genMasterKeyFromXPrv = Internal.genMasterKeyFromXPrv
  
  -- Re-export from 'Cardano.Address.Derivation' to have it documented specialized in Haddock.
  --
  -- | Derives an account private key from the given root private key.
  --
  -- @since 3.4.0
  deriveAccountPrivateKey
      :: Shared 'RootK XPrv
      -> Index 'Hardened 'AccountK
      -> Shared 'AccountK XPrv
  deriveAccountPrivateKey = Internal.deriveAccountPrivateKey
  
  -- Re-export from 'Cardano.Address.Derivation' to have it documented specialized in Haddock.
  --
  -- | Derives a multisig private key from the given account private key for payment credential.
  --
  -- @since 3.4.0
  deriveAddressPrivateKey
      :: Shared 'AccountK XPrv
      -> Role
      -> Index 'Soft 'PaymentK
      -> Shared 'ScriptK XPrv
  deriveAddressPrivateKey = coerce . Internal.deriveAddressPrivateKey
  
  -- Re-export from 'Cardano.Address.Derivation' to have it documented specialized in Haddock.
  --
  -- | Derives a multisig private key from the given account private key for delegation credential.
  --
  -- @since 3.4.0
  deriveDelegationPrivateKey
      :: Shared 'AccountK XPrv
      -> Index 'Soft 'PaymentK
      -> Shared 'ScriptK XPrv
  deriveDelegationPrivateKey accPrv = coerce .
      Internal.deriveAddressPrivateKey accPrv Stake
  
  -- Re-export from 'Cardano.Address.Derivation' to have it documented specialized in Haddock
  --
  -- | Derives a multisig public key from the given account public key for payment credential.
  --
  -- @since 3.4.0
  deriveAddressPublicKey
      :: Shared 'AccountK XPub
      -> Role
      -> Index 'Soft 'PaymentK
      -> Shared 'ScriptK XPub
  deriveAddressPublicKey = coerce . Internal.deriveAddressPublicKey
  
  -- Re-export from 'Cardano.Address.Derivation' to have it documented specialized in Haddock
  --
  -- | Derives a multisig public key from the given account public key for delegation credential.
  --
  -- @since 3.4.0
  deriveDelegationPublicKey
      :: Shared 'AccountK XPub
      -> Index 'Soft 'PaymentK
      -> Shared 'ScriptK XPub
  deriveDelegationPublicKey accPub = coerce .
      Internal.deriveAddressPublicKey accPub Stake
  
  --
  -- Unsafe
  --
  
  -- | Unsafe backdoor for constructing an 'Shared' key from a raw 'XPrv'. this is
  -- unsafe because it lets the caller choose the actually derivation 'depth'.
  --
  -- This can be useful however when serializing / deserializing such a type, or to
  -- speed up test code (and avoid having to do needless derivations from a master
  -- key down to an address key for instance).
  --
  -- @since 3.4.0
  liftXPrv :: XPrv -> Shared depth XPrv
  liftXPrv = Shared
  
  -- | Unsafe backdoor for constructing an 'Shared' key from a raw 'XPub'. this is
  -- unsafe because it lets the caller choose the actually derivation 'depth'.
  --
  -- This can be useful however when serializing / deserializing such a type, or to
  -- speed up test code (and avoid having to do needless derivations from a master
  -- key down to an address key for instance).
  --
  -- @since 3.4.0
  liftXPub :: XPub -> Shared depth XPub
  liftXPub = Shared
  
  
  -- | Calculates wallet id of shared wallet
  -- It takes raw bytes of account public kye (64-bytes),
  -- spending script template, and
  -- optionally staking script template.
  --
  -- @since 3.10.0
  sharedWalletId
      :: ByteString
      -> Script Cosigner
      -> Maybe (Script Cosigner)
      -> ByteString
  sharedWalletId bytes spending stakingM =
      if BS.length bytes == 64 then
          hashWalletId $
          bytes <>
          serializeScriptTemplate spending <>
          maybe mempty serializeScriptTemplate stakingM
      else
          error "Extended account public key is expected to have 64 bytes."
    where
      serializeScriptTemplate = T.encodeUtf8 . scriptToText
  
  --
  -- Internal
  --
  
  --- | Computes a 28-byte Blake2b224 digest of a Shared 'XPub'.
  ---
  --- @since 3.4.0
  hashKey :: KeyRole -> Shared key XPub -> KeyHash
  hashKey cred = KeyHash cred . hashCredential . xpubPublicKey . getKey
  
  -- | Purpose is a constant set to 1854' (or 0x8000073e) following the
  -- CIP-1854 Multi-signatures HD Wallets
  --
  -- Hardened derivation is used at this level.
  purposeIndex :: Word32
  purposeIndex = 0x8000073e
  