packages feed

cardano-addresses-4.0.0: test/Command/KeySpec.hs

{-# LANGUAGE FlexibleContexts #-}

module Command.KeySpec
    ( spec
    ) where

import Prelude

import Cardano.Address.Derivation
    ( XPrv, xprvToBytes )
import Codec.Binary.Encoding
    ( AbstractEncoding (..), encode )
import Data.List
    ( isInfixOf )
import Options.Applicative.Derivation
    ( DerivationIndex, derivationIndexToString, firstHardened )
import Test.Hspec
    ( Spec )
import Test.Hspec.QuickCheck
    ( prop )
import Test.QuickCheck
    ( Property, counterexample, label )
import Test.QuickCheck.Monadic
    ( assert, monadicIO, monitor, run )
import Test.Utils
    ( cli, describeCmd )

import Test.Arbitrary
    ()

import qualified Cardano.Codec.Bech32.Prefixes as CIP5
import qualified Data.Text as T
import qualified Data.Text.Encoding as T

spec :: Spec
spec = describeCmd ["key"] $ do
    prop "public/child commute" prop_publicKeyDerivation

-- | For soft indices, public key derivation should be "equivalent" to private
-- key derivation.
--
-- I.e. The following diagram should commute:
--
-- @
--                        key public
--
--              xprv +-----------------> xpub
--                +                       +
--                |                       |
--                |                       |
--      key child |                       | key child
--                |                       |
--                |                       |
--                v                       v
--             xprv' +-----------------> xpub'
--
--                        key public
-- @
prop_publicKeyDerivation
    :: DerivationIndex
    -> XPrv
    -> Property
prop_publicKeyDerivation ix xprv = do
    if ix < firstHardened then monadicIO $ do
        monitor (label "Soft Index")
        out1 <- run (public bytes >>= child)
        out2 <- run (child  bytes >>= public)
        monitor (counterexample ("stdout: " <> out1))
        monitor (counterexample ("stdout: " <> out2))
        assert (out1 == out2)
    else monadicIO $ do
        monitor (label "Hard Index")
        (out, err) <- run (public bytes >>= cli ["key", "child", "0/" <> derivationIndexToString ix ])
        monitor (counterexample ("stdout: " <> out))
        monitor (counterexample ("stderr: " <> err))
        assert (out == "")
        assert ("you must use soft indexes only" `isInfixOf` err)
  where
    bytes  = T.unpack $ T.decodeUtf8 $ encode (EBech32 CIP5.acct_xsk) $ xprvToBytes xprv
    public = cli [ "key", "public", "--with-chain-code" ]
    child  = cli [ "key", "child", "0/" <> derivationIndexToString ix ]