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