cardano-addresses-4.0.0: lib/Cardano/Address/Style/Shared.hs
{-# 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