never executed always true always false
    1 {-# LANGUAGE FlexibleContexts #-}
    2 {-# LANGUAGE NamedFieldPuns #-}
    3 {-# LANGUAGE OverloadedStrings #-}
    4 {-# LANGUAGE TypeSynonymInstances #-}
    5 
    6 {-# OPTIONS_HADDOCK hide #-}
    7 
    8 module Command.Key.WalletId
    9     ( Cmd (..)
   10     , mod
   11     , run
   12     ) where
   13 
   14 import Prelude hiding
   15     ( mod )
   16 
   17 import Cardano.Address.Derivation
   18     ( hashWalletId, toXPub, xprvFromBytes, xpubToBytes )
   19 import Cardano.Address.Script
   20     ( Cosigner, Script )
   21 import Cardano.Address.Style.Shared
   22     ( sharedWalletId )
   23 import Codec.Binary.Encoding
   24     ( AbstractEncoding (..) )
   25 import Control.Monad
   26     ( when )
   27 import Data.Maybe
   28     ( fromJust, isNothing )
   29 import Options.Applicative
   30     ( CommandFields
   31     , Mod
   32     , command
   33     , footerDoc
   34     , helper
   35     , info
   36     , optional
   37     , progDesc
   38     )
   39 import Options.Applicative.Help.Pretty
   40     ( string )
   41 import Options.Applicative.Script
   42     ( scriptTemplateSpendingArg, scriptTemplateStakingArg )
   43 import System.IO
   44     ( stdin, stdout )
   45 import System.IO.Extra
   46     ( hGetBech32, hPutBytes )
   47 
   48 import qualified Cardano.Codec.Bech32.Prefixes as CIP5
   49 import qualified Data.ByteString as BS
   50 
   51 data Cmd = WalletId
   52     { spending :: Maybe (Script Cosigner)
   53     , staking :: Maybe (Script Cosigner)
   54     } deriving (Show)
   55 
   56 mod :: (Cmd -> parent) -> Mod CommandFields parent
   57 mod liftCmd = command "walletid" $
   58     info (helper <*> fmap liftCmd parser) $ mempty
   59         <> progDesc "Shows the cardano-wallet wallet ID for a given key"
   60         <> footerDoc (Just $ string $ mconcat
   61             [ "A wallet ID is a 40-digit hexadecimal string derived "
   62             , "from the wallet’s key. It is used by the cardano-wallet "
   63             , "server to refer to specific wallets.\n\n"
   64             , "For shelley wallets the key can be either an extended root key "
   65             , "(full multi-account wallets), or an extended account key "
   66             , "(single-account wallets).\n\n"
   67             , "In the latter case either private or public extended key is accepted "
   68             , "-- they will have the same wallet ID.\n\n"
   69             , "The bech32-encoded key is read from standard input.\n\n"
   70             , "In case of a shared wallet, the wallet id is calculated based on "
   71             , "private extended account key, payment template script and "
   72             , "staking template script - if specified. Each signature in "
   73             , "any template script is denoted by cosigner#number.\n"
   74             ])
   75   where
   76     parser = WalletId
   77         <$> optional scriptTemplateSpendingArg
   78         <*> optional scriptTemplateStakingArg
   79 
   80 run :: Cmd -> IO ()
   81 run WalletId{spending,staking} = do
   82     (hrp, bytes) <- hGetBech32 stdin allowedPrefixes
   83     guardBytes hrp bytes
   84     let bs = payloadToHash hrp bytes
   85     when ( hrp `elem` [CIP5.acct_shared_xvk, CIP5.acct_shared_xsk] &&
   86            isNothing spending ) $
   87         fail "shared wallet needs to have at least spending script specified"
   88     let walletid =
   89             if hrp `elem` [CIP5.acct_shared_xvk, CIP5.acct_shared_xsk] then
   90                 sharedWalletId bs (fromJust spending) staking
   91             else
   92                 hashWalletId bs
   93     hPutBytes stdout walletid EBase16
   94   where
   95     allowedPrefixes =
   96         [ CIP5.root_xsk
   97         , CIP5.root_xvk
   98         , CIP5.acct_xvk
   99         , CIP5.acct_xsk
  100         , CIP5.acct_shared_xvk
  101         , CIP5.acct_shared_xsk
  102         ]
  103 
  104     payloadToHash hrp bs
  105         | hrp `elem` [CIP5.root_xsk, CIP5.acct_xsk, CIP5.acct_shared_xsk] =
  106               case xprvFromBytes bs of
  107                   Just xprv ->  xpubToBytes . toXPub $ xprv
  108                   Nothing -> "96-byte extended private key is invalid due to 'scalarDecodeLong' failure from cryptonite"
  109         | otherwise =
  110               bs
  111 
  112     guardBytes hrp bytes
  113         | hrp `elem` [CIP5.root_xsk, CIP5.root_shared_xsk, CIP5.acct_xsk, CIP5.acct_shared_xsk] = do
  114             when (BS.length bytes /= 96) $
  115                 fail "data should be a 96-byte private key."
  116 
  117         | otherwise = do
  118             when (BS.length bytes /= 64) $
  119                 fail "data should be a 32-byte public key with a 32-byte chain-code appended."