Skip to content

Commit b38f750

Browse files
authored
Merge pull request #975 from IntersectMBO/add-mnemonic-support
Add mnemonic sentence support
2 parents deaa3d1 + 5a25251 commit b38f750

31 files changed

+1111
-2
lines changed

cardano-cli/cardano-cli.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ library
181181
Cardano.CLI.Read
182182
Cardano.CLI.Render
183183
Cardano.CLI.Run
184+
Cardano.CLI.Run.Mnemonic
184185
Cardano.CLI.TopHandler
185186
Cardano.CLI.Type.Common
186187
Cardano.CLI.Type.Error.AddressCmdError
@@ -256,6 +257,7 @@ library
256257
exceptions,
257258
filepath,
258259
formatting,
260+
haskeline,
259261
http-client,
260262
http-client-tls,
261263
http-types,

cardano-cli/src/Cardano/CLI/EraIndependent/Key/Command.hs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ module Cardano.CLI.EraIndependent.Key.Command
66
( KeyCmds (..)
77
, KeyVerificationKeyCmdArgs (..)
88
, KeyNonExtendedKeyCmdArgs (..)
9+
, KeyGenerateMnemonicCmdArgs (..)
10+
, KeyExtendedSigningKeyFromMnemonicArgs (..)
11+
, ExtendedSigningType (..)
12+
, MnemonicSource (..)
913
, KeyConvertByronKeyCmdArgs (..)
1014
, KeyConvertByronGenesisVKeyCmdArgs (..)
1115
, KeyConvertITNKeyCmdArgs (..)
@@ -19,12 +23,15 @@ where
1923
import Cardano.Api.Shelley
2024

2125
import Cardano.CLI.Type.Common
26+
import Cardano.Prelude (Word32)
2227

2328
import Data.Text (Text)
2429

2530
data KeyCmds
2631
= KeyVerificationKeyCmd !KeyVerificationKeyCmdArgs
2732
| KeyNonExtendedKeyCmd !KeyNonExtendedKeyCmdArgs
33+
| KeyGenerateMnemonicCmd !KeyGenerateMnemonicCmdArgs
34+
| KeyExtendedSigningKeyFromMnemonicCmd !KeyExtendedSigningKeyFromMnemonicArgs
2835
| KeyConvertByronKeyCmd !KeyConvertByronKeyCmdArgs
2936
| KeyConvertByronGenesisVKeyCmd !KeyConvertByronGenesisVKeyCmdArgs
3037
| KeyConvertITNKeyCmd !KeyConvertITNKeyCmdArgs
@@ -52,6 +59,41 @@ data KeyNonExtendedKeyCmdArgs = KeyNonExtendedKeyCmdArgs
5259
}
5360
deriving Show
5461

62+
-- | Generate a mnemonic phrase that can be used to derive signing keys.
63+
data KeyGenerateMnemonicCmdArgs = KeyGenerateMnemonicCmdArgs
64+
{ mnemonicOutputFormat :: !(Maybe (File () Out))
65+
-- ^ Output format for the mnemonic phrase
66+
, mnemonicWords :: !MnemonicSize
67+
-- ^ Number of mnemonic words to generate it must be one of: 12, 15, 18, 21, or 24.
68+
}
69+
deriving Show
70+
71+
-- | Get an extended signing key from a mnemonic.
72+
data KeyExtendedSigningKeyFromMnemonicArgs = KeyExtendedSigningKeyFromMnemonicArgs
73+
{ keyOutputFormat :: !KeyOutputFormat
74+
, derivedExtendedSigningKeyType :: !ExtendedSigningType
75+
, derivationAccountNo :: !Word32
76+
, mnemonicSource :: !MnemonicSource
77+
, signingKeyFileOut :: !(SigningKeyFile Out)
78+
}
79+
deriving Show
80+
81+
data MnemonicSource
82+
= MnemonicFromFile !(File () In)
83+
| MnemonicFromInteractivePrompt
84+
deriving Show
85+
86+
-- | Type of the key derived from a mnemonic
87+
-- together with the payment key number in the derivation path
88+
-- for cases where it is applicable.
89+
data ExtendedSigningType
90+
= ExtendedSigningPaymentKey !Word32
91+
| ExtendedSigningStakeKey !Word32
92+
| ExtendedSigningDRepKey
93+
| ExtendedSigningCCColdKey
94+
| ExtendedSigningCCHotKey
95+
deriving Show
96+
5597
-- | Convert a Byron payment, genesis or genesis delegate key (signing or
5698
-- verification) to a corresponding Shelley-format key.
5799
data KeyConvertByronKeyCmdArgs = KeyConvertByronKeyCmdArgs
@@ -124,6 +166,10 @@ renderKeyCmds = \case
124166
"key verification-key"
125167
KeyNonExtendedKeyCmd{} ->
126168
"key non-extended-key"
169+
KeyGenerateMnemonicCmd{} ->
170+
"key generate-mnemonic"
171+
KeyExtendedSigningKeyFromMnemonicCmd{} ->
172+
"key from-mnemonic"
127173
KeyConvertByronKeyCmd{} ->
128174
"key convert-byron-key"
129175
KeyConvertByronGenesisVKeyCmd{} ->

cardano-cli/src/Cardano/CLI/EraIndependent/Key/Option.hs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE DataKinds #-}
22
{-# LANGUAGE FlexibleContexts #-}
33
{-# LANGUAGE GADTs #-}
4+
{-# LANGUAGE LambdaCase #-}
45
{-# LANGUAGE ScopedTypeVariables #-}
56

67
module Cardano.CLI.EraIndependent.Key.Option
@@ -17,8 +18,10 @@ import Cardano.CLI.Type.Common
1718

1819
import Data.Foldable
1920
import Data.Text (Text)
21+
import GHC.Word (Word32)
2022
import Options.Applicative hiding (help, str)
2123
import Options.Applicative qualified as Opt
24+
import Options.Applicative.Types (readerAsk)
2225

2326
{- HLINT ignore "Use <$>" -}
2427
{- HLINT ignore "Move brackets to avoid $" -}
@@ -42,6 +45,23 @@ pKeyCmds =
4245
, "extended verification key. This supports all "
4346
, "extended key types."
4447
]
48+
, subParser "generate-mnemonic" $
49+
Opt.info pKeyGenerateMnemonicCmd $
50+
Opt.progDesc $
51+
mconcat
52+
[ "Generate a mnemonic sentence that can be used "
53+
, "for key derivation."
54+
]
55+
, subParser "derive-from-mnemonic" $
56+
Opt.info pKeyExtendedSigningKeyFromMnemonicCmd $
57+
Opt.progDesc $
58+
mconcat
59+
[ "Derive an extended signing key from a mnemonic "
60+
, "sentence. "
61+
, "To ensure the safety of the mnemonic phrase, "
62+
, "we recommend that key derivation is performed "
63+
, "in an air-gapped environment."
64+
]
4565
, subParser "convert-byron-key" $
4666
Opt.info pKeyConvertByronKeyCmd $
4767
Opt.progDesc $
@@ -114,6 +134,107 @@ pKeyNonExtendedKeyCmd =
114134
<$> pExtendedVerificationKeyFileIn
115135
<*> pVerificationKeyFileOut
116136

137+
pKeyGenerateMnemonicCmd :: Parser KeyCmds
138+
pKeyGenerateMnemonicCmd =
139+
fmap KeyGenerateMnemonicCmd $
140+
KeyGenerateMnemonicCmdArgs
141+
<$> optional pOutputFile
142+
<*> pMnemonicSize
143+
144+
pMnemonicSize :: Parser MnemonicSize
145+
pMnemonicSize = do
146+
option
147+
parseSize
148+
( long "size"
149+
<> metavar "WORD32"
150+
<> Opt.help
151+
( mconcat
152+
[ "Specify the desired number of words for the output"
153+
, "mnemonic sentence (valid options are: 12, 15, 18, 21, and 24)"
154+
]
155+
)
156+
)
157+
where
158+
parseSize :: ReadM MnemonicSize
159+
parseSize =
160+
readerAsk
161+
>>= \case
162+
"12" -> return MS12
163+
"15" -> return MS15
164+
"18" -> return MS18
165+
"21" -> return MS21
166+
"24" -> return MS24
167+
invalidSize ->
168+
readerError $
169+
"Invalid mnemonic size " <> show invalidSize <> "! It must be one of: 12, 15, 18, 21, or 24."
170+
171+
pKeyExtendedSigningKeyFromMnemonicCmd :: Parser KeyCmds
172+
pKeyExtendedSigningKeyFromMnemonicCmd =
173+
fmap KeyExtendedSigningKeyFromMnemonicCmd $
174+
KeyExtendedSigningKeyFromMnemonicArgs
175+
<$> pKeyOutputFormat
176+
<*> pDerivedExtendedSigningKeyType
177+
<*> pAccountNumber
178+
<*> pMnemonicSource
179+
<*> pSigningKeyFileOut
180+
181+
pDerivedExtendedSigningKeyType :: Parser ExtendedSigningType
182+
pDerivedExtendedSigningKeyType =
183+
asum
184+
[ Opt.option (ExtendedSigningPaymentKey <$> integralReader) $
185+
mconcat
186+
[ Opt.long "payment-key-with-number"
187+
, Opt.metavar "WORD32"
188+
, Opt.help
189+
"Derive an extended payment key with the given payment address number from the derivation path."
190+
]
191+
, Opt.option (ExtendedSigningStakeKey <$> integralReader) $
192+
mconcat
193+
[ Opt.long "stake-key-with-number"
194+
, Opt.metavar "WORD32"
195+
, Opt.help
196+
"Derive an extended stake key with the given stake address number from the derivation path."
197+
]
198+
, Opt.flag' ExtendedSigningDRepKey $
199+
mconcat
200+
[ Opt.long "drep-key"
201+
, Opt.help "Derive an extended DRep key."
202+
]
203+
, Opt.flag' ExtendedSigningCCColdKey $
204+
mconcat
205+
[ Opt.long "cc-cold-key"
206+
, Opt.help "Derive an extended committee cold key."
207+
]
208+
, Opt.flag' ExtendedSigningCCHotKey $
209+
mconcat
210+
[ Opt.long "cc-hot-key"
211+
, Opt.help "Derive an extended committee hot key."
212+
]
213+
]
214+
215+
pMnemonicSource :: Parser MnemonicSource
216+
pMnemonicSource =
217+
asum
218+
[ MnemonicFromFile . File <$> parseFilePath "mnemonic-from-file" "Input text file with the mnemonic."
219+
, Opt.flag' MnemonicFromInteractivePrompt $
220+
mconcat
221+
[ Opt.long "mnemonic-from-interactive-prompt"
222+
, Opt.help $
223+
"Input the mnemonic through an interactive prompt. "
224+
<> "This mode also accepts receiving the mnemonic through "
225+
<> "standard input directly, for example, by using a pipe."
226+
]
227+
]
228+
229+
pAccountNumber :: Parser Word32
230+
pAccountNumber =
231+
Opt.option integralReader $
232+
mconcat
233+
[ Opt.long "account-number"
234+
, Opt.metavar "WORD32"
235+
, Opt.help "Account number in the derivation path."
236+
]
237+
117238
pKeyConvertByronKeyCmd :: Parser KeyCmds
118239
pKeyConvertByronKeyCmd =
119240
fmap KeyConvertByronKeyCmd $

cardano-cli/src/Cardano/CLI/EraIndependent/Key/Run.hs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
{-# LANGUAGE DataKinds #-}
22
{-# LANGUAGE DuplicateRecordFields #-}
3-
{-# LANGUAGE FlexibleContexts #-}
43
{-# LANGUAGE LambdaCase #-}
54
{-# LANGUAGE NamedFieldPuns #-}
6-
{-# LANGUAGE RankNTypes #-}
75
{-# LANGUAGE ScopedTypeVariables #-}
86

97
module Cardano.CLI.EraIndependent.Key.Run
@@ -41,6 +39,7 @@ import Cardano.Api.Shelley (StakePoolKey)
4139

4240
import Cardano.CLI.Byron.Key qualified as Byron
4341
import Cardano.CLI.EraIndependent.Key.Command qualified as Cmd
42+
import Cardano.CLI.Run.Mnemonic qualified as Mnemonic
4443
import Cardano.CLI.Type.Common
4544
import Cardano.CLI.Type.Error.CardanoAddressSigningKeyConversionError
4645
import Cardano.CLI.Type.Error.ItnKeyConversionError
@@ -117,6 +116,10 @@ runKeyCmds = \case
117116
runVerificationKeyCmd cmd
118117
Cmd.KeyNonExtendedKeyCmd cmd ->
119118
runNonExtendedKeyCmd cmd
119+
Cmd.KeyGenerateMnemonicCmd cmd ->
120+
runGenerateMnemonicCmd cmd
121+
Cmd.KeyExtendedSigningKeyFromMnemonicCmd cmd ->
122+
runExtendedSigningKeyFromMnemonicCmd cmd
120123
Cmd.KeyConvertByronKeyCmd cmd ->
121124
runConvertByronKeyCmd cmd
122125
Cmd.KeyConvertByronGenesisVKeyCmd cmd ->
@@ -210,6 +213,13 @@ runNonExtendedKeyCmd
210213
writeLazyByteStringFile vkf' $
211214
textEnvelopeToJSON descr vk
212215

216+
runGenerateMnemonicCmd :: Cmd.KeyGenerateMnemonicCmdArgs -> ExceptT KeyCmdError IO ()
217+
runGenerateMnemonicCmd
218+
Cmd.KeyGenerateMnemonicCmdArgs
219+
{ mnemonicOutputFormat
220+
, mnemonicWords
221+
} = Mnemonic.generateMnemonic mnemonicWords mnemonicOutputFormat
222+
213223
readExtendedVerificationKeyFile
214224
:: VerificationKeyFile In
215225
-> ExceptT KeyCmdError IO SomeAddressVerificationKey
@@ -242,6 +252,24 @@ readExtendedVerificationKeyFile evkfile = do
242252
where
243253
goFail k = left $ KeyCmdExpectedExtendedVerificationKey k
244254

255+
runExtendedSigningKeyFromMnemonicCmd
256+
:: Cmd.KeyExtendedSigningKeyFromMnemonicArgs
257+
-> ExceptT KeyCmdError IO ()
258+
runExtendedSigningKeyFromMnemonicCmd
259+
Cmd.KeyExtendedSigningKeyFromMnemonicArgs
260+
{ keyOutputFormat
261+
, derivedExtendedSigningKeyType
262+
, derivationAccountNo
263+
, mnemonicSource
264+
, signingKeyFileOut
265+
} =
266+
Mnemonic.extendedSigningKeyFromMnemonicImpl
267+
keyOutputFormat
268+
derivedExtendedSigningKeyType
269+
derivationAccountNo
270+
mnemonicSource
271+
signingKeyFileOut
272+
245273
runConvertByronKeyCmd
246274
:: Cmd.KeyConvertByronKeyCmdArgs
247275
-> ExceptT KeyCmdError IO ()

0 commit comments

Comments
 (0)