Skip to content

Commit

Permalink
Minor refactoring of ITN key conversion command code
Browse files Browse the repository at this point in the history
  • Loading branch information
intricate committed Sep 8, 2020
1 parent 5e59e47 commit 8bcae8e
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 54 deletions.
82 changes: 32 additions & 50 deletions cardano-cli/src/Cardano/CLI/Shelley/Run/Key.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Cardano.CLI.Shelley.Run.Key
, runKeyCmd

-- * Exports for testing
, decodeBech32Key
, decodeBech32
) where

import Cardano.Prelude
Expand Down Expand Up @@ -49,7 +49,7 @@ data ShelleyKeyCmdError
!Text
-- ^ Text representation of the parse error. Unfortunately, the actual
-- error type isn't exported.
| ShelleyKeyCmdItnKeyConvError !ConversionError
| ShelleyKeyCmdItnKeyConvError !ItnKeyConversionError
| ShelleyKeyCmdWrongKeyTypeError
| ShelleyKeyCmdCardanoAddressSigningKeyFileError
!(FileError CardanoAddressSigningKeyConversionError)
Expand Down Expand Up @@ -421,88 +421,70 @@ runConvertITNBip32ToStakeKey (ASigningKeyFile (SigningKeyFile sk)) (OutputFile o
firstExceptT ShelleyKeyCmdWriteFileError . newExceptT
$ writeFileTextEnvelope outFile Nothing skey

data ConversionError
= Bech32KeyDecodingError
-- ^ Bech32 key
!Text
!Bech32.DecodingError
| Bech32ErrorExtractingByes !Bech32.DataPart
| Bech32ReadError !FilePath !IOException
| ITNError !Bech32.HumanReadablePart !Bech32.DataPart
| SigningKeyDeserializationError !ByteString
| VerificationKeyDeserializationError !ByteString
-- | An error that can occur while converting an Incentivized Testnet (ITN)
-- key.
data ItnKeyConversionError
= ItnKeyBech32DecodeError !Bech32DecodeError
| ItnReadBech32FileError !FilePath !IOException
| ItnSigningKeyDeserialisationError !ByteString
| ItnVerificationKeyDeserialisationError !ByteString
deriving Show

renderConversionError :: ConversionError -> Text
-- | Render an error message for an 'ItnKeyConversionError'.
renderConversionError :: ItnKeyConversionError -> Text
renderConversionError err =
case err of
Bech32KeyDecodingError key decErr ->
"Error decoding Bech32 key: " <> key <> " Error: " <> textShow decErr
Bech32ErrorExtractingByes dp ->
"Unable to extract bytes from: " <> Bech32.dataPartToText dp
Bech32ReadError fp readErr ->
"Error reading bech32 key at: " <> textShow fp
ItnKeyBech32DecodeError decErr ->
"Error decoding Bech32 key: " <> Text.pack (displayError decErr)
ItnReadBech32FileError fp readErr ->
"Error reading Bech32 key at: " <> textShow fp
<> " Error: " <> Text.pack (displayException readErr)
ITNError hRpart dp ->
"Error extracting a ByteString from DataPart: " <> Bech32.dataPartToText dp <>
" With human readable part: " <> Bech32.humanReadablePartToText hRpart
SigningKeyDeserializationError sKey ->
ItnSigningKeyDeserialisationError sKey ->
"Error deserialising signing key: " <> textShow (BSC.unpack sKey)
VerificationKeyDeserializationError vKey ->
ItnVerificationKeyDeserialisationError vKey ->
"Error deserialising verification key: " <> textShow (BSC.unpack vKey)

-- | Convert public ed25519 key to a Shelley stake verification key
convertITNVerificationKey :: Text -> Either ConversionError (VerificationKey StakeKey)
convertITNVerificationKey :: Text -> Either ItnKeyConversionError (VerificationKey StakeKey)
convertITNVerificationKey pubKey = do
(_, _, keyBS) <- decodeBech32Key pubKey
(_, _, keyBS) <- first ItnKeyBech32DecodeError (decodeBech32 pubKey)
case DSIGN.rawDeserialiseVerKeyDSIGN keyBS of
Just verKey -> Right . StakeVerificationKey $ Shelley.VKey verKey
Nothing -> Left $ VerificationKeyDeserializationError keyBS
Nothing -> Left $ ItnVerificationKeyDeserialisationError keyBS

-- | Convert private ed22519 key to a Shelley signing key.
convertITNSigningKey :: Text -> Either ConversionError (SigningKey StakeKey)
convertITNSigningKey :: Text -> Either ItnKeyConversionError (SigningKey StakeKey)
convertITNSigningKey privKey = do
(_, _, keyBS) <- decodeBech32Key privKey
(_, _, keyBS) <- first ItnKeyBech32DecodeError (decodeBech32 privKey)
case DSIGN.rawDeserialiseSignKeyDSIGN keyBS of
Just signKey -> Right $ StakeSigningKey signKey
Nothing -> Left $ SigningKeyDeserializationError keyBS
Nothing -> Left $ ItnSigningKeyDeserialisationError keyBS

-- | Convert extended private ed22519 key to a Shelley signing key
-- Extended private key = 64 bytes,
-- Public key = 32 bytes.
convertITNExtendedSigningKey :: Text -> Either ConversionError (SigningKey StakeExtendedKey)
convertITNExtendedSigningKey :: Text -> Either ItnKeyConversionError (SigningKey StakeExtendedKey)
convertITNExtendedSigningKey privKey = do
(_, _, privkeyBS) <- decodeBech32Key privKey
(_, _, privkeyBS) <- first ItnKeyBech32DecodeError (decodeBech32 privKey)
let dummyChainCode = BS.replicate 32 0
case xprvFromBytes $ BS.concat [privkeyBS, dummyChainCode] of
Just xprv -> Right $ StakeExtendedSigningKey xprv
Nothing -> Left $ SigningKeyDeserializationError privkeyBS
Nothing -> Left $ ItnSigningKeyDeserialisationError privkeyBS

-- BIP32 Private key = 96 bytes (64 bytes extended private key + 32 bytes chaincode)
-- BIP32 Public Key = 64 Bytes
convertITNBIP32SigningKey :: Text -> Either ConversionError (SigningKey StakeExtendedKey)
convertITNBIP32SigningKey :: Text -> Either ItnKeyConversionError (SigningKey StakeExtendedKey)
convertITNBIP32SigningKey privKey = do
(_, _, privkeyBS) <- decodeBech32Key privKey
(_, _, privkeyBS) <- first ItnKeyBech32DecodeError (decodeBech32 privKey)
case xprvFromBytes privkeyBS of
Just xprv -> Right $ StakeExtendedSigningKey xprv
Nothing -> Left $ SigningKeyDeserializationError privkeyBS

-- | Convert ITN Bech32 public or private keys to 'ByteString's
decodeBech32Key :: Text
-> Either ConversionError
(Bech32.HumanReadablePart, Bech32.DataPart, ByteString)
decodeBech32Key key =
case Bech32.decodeLenient key of
Left err -> Left $ Bech32KeyDecodingError key err
Right (hRpart, dataPart) -> case Bech32.dataPartToBytes dataPart of
Nothing -> Left $ ITNError hRpart dataPart
Just bs -> Right (hRpart, dataPart, bs)

readFileITNKey :: FilePath -> IO (Either ConversionError Text)
Nothing -> Left $ ItnSigningKeyDeserialisationError privkeyBS

readFileITNKey :: FilePath -> IO (Either ItnKeyConversionError Text)
readFileITNKey fp = do
eStr <- Exception.try $ readFile fp
case eStr of
Left e -> return . Left $ Bech32ReadError fp e
Left e -> return . Left $ ItnReadBech32FileError fp e
Right str -> return . Right . Text.concat $ Text.words str

--------------------------------------------------------------------------------
Expand Down
8 changes: 4 additions & 4 deletions cardano-cli/test/Test/Cli/ITN.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Test.Cli.ITN
( tests
) where

import Cardano.CLI.Shelley.Run.Key (decodeBech32Key)
import Cardano.CLI.Shelley.Run.Key (decodeBech32)
import Cardano.Prelude
import Hedgehog (Property, (===))
import Test.OptParse
Expand Down Expand Up @@ -112,14 +112,14 @@ prop_convertITNBIP32SigningKey = propertyOnce . moduleWorkspace "tmp" $ \tempDir
-- Check for existence of the converted ITN keys
assertFilesExist [outputHaskellSignKeyFp]

-- | We check our 'decodeBech32Key' outputs against https://slowli.github.io/bech32-buffer/
-- | We check our 'decodeBech32' outputs against https://slowli.github.io/bech32-buffer/
-- using 'itnVerKey' & 'itnSignKey' as inputs.
golden_bech32Decode :: Property
golden_bech32Decode = propertyOnce $ do
(vHumReadPart, vDataPart , _) <- H.evalEither $ decodeBech32Key itnVerKey
(vHumReadPart, vDataPart , _) <- H.evalEither $ decodeBech32 itnVerKey
Just vDataPartBase16 <- pure (dataPartToBase16 vDataPart)

(sHumReadPart, sDataPart , _) <- H.evalEither $ decodeBech32Key itnSignKey
(sHumReadPart, sDataPart , _) <- H.evalEither $ decodeBech32 itnSignKey
Just sDataPartBase16 <- pure (dataPartToBase16 sDataPart)

-- Based on https://slowli.github.io/bech32-buffer/ which are in Base16
Expand Down

0 comments on commit 8bcae8e

Please sign in to comment.