{-# 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 ]