-
Notifications
You must be signed in to change notification settings - Fork 213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port mnemonic module #47
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
{-# LANGUAGE DataKinds #-} | ||
|
||
-- | | ||
-- Copyright: © 2018-2019 IOHK | ||
-- License: MIT | ||
-- | ||
-- | Mnemonic generation executable | ||
|
||
|
||
import Prelude | ||
|
||
import Cardano.Wallet.Mnemonic | ||
( Mnemonic, entropyToMnemonic, genEntropy, mnemonicToText ) | ||
import Data.Function | ||
( flip ) | ||
import Data.Text | ||
( Text ) | ||
|
||
import qualified Data.Text as T | ||
import qualified Data.Text.IO as T | ||
|
||
main | ||
:: IO () | ||
main = do | ||
backupPhrase <- generateBackupPhrase | ||
let backupPhraseString = backupPhraseToString backupPhrase | ||
T.putStrLn $ formatOutput backupPhraseString | ||
|
||
generateBackupPhrase | ||
:: IO (Mnemonic 15) | ||
generateBackupPhrase = | ||
entropyToMnemonic <$> genEntropy | ||
|
||
backupPhraseToString | ||
:: Mnemonic 15 | ||
-> [Text] | ||
backupPhraseToString = mnemonicToText | ||
|
||
formatOutput | ||
:: [Text] | ||
-> Text | ||
formatOutput = | ||
flip T.snoc ']' | ||
. T.append "[" | ||
. T.intercalate "," | ||
. map (T.append "\"" . flip T.snoc '"' ) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
{-# LANGUAGE AllowAmbiguousTypes #-} | ||
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE FlexibleInstances #-} | ||
{-# LANGUAGE RankNTypes #-} | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
{-# LANGUAGE StandaloneDeriving #-} | ||
{-# LANGUAGE TypeApplications #-} | ||
{-# LANGUAGE TypeFamilies #-} | ||
{-# LANGUAGE TypeOperators #-} | ||
{-# LANGUAGE UndecidableInstances #-} | ||
{-# OPTIONS_GHC -fno-warn-orphans #-} | ||
|
||
-- | | ||
-- Copyright: © 2018-2019 IOHK | ||
-- License: MIT | ||
-- | ||
-- | Module provides mnemonic creation and | ||
-- | restoring from backup phrase functionality | ||
|
||
module Cardano.Wallet.Mnemonic | ||
( | ||
-- * Types | ||
Mnemonic | ||
, Entropy | ||
, EntropySize | ||
, MnemonicWords | ||
|
||
-- * Errors | ||
, MnemonicError(..) | ||
, MnemonicException(..) | ||
, EntropyError(..) | ||
, DictionaryError(..) | ||
, MnemonicWordsError(..) | ||
|
||
-- * Creating @Mnemonic@ (resp. @Entropy@) | ||
, mkEntropy | ||
, mkMnemonic | ||
, genEntropy | ||
|
||
-- * Converting from and to @Mnemonic@ (resp. @Entropy@) | ||
, mnemonicToEntropy | ||
, entropyToMnemonic | ||
, entropyToByteString | ||
|
||
, ambiguousNatVal | ||
, mnemonicToText | ||
) where | ||
|
||
import Prelude | ||
|
||
import Basement.Sized.List | ||
( unListN ) | ||
import Control.Arrow | ||
( left ) | ||
import Control.Monad.Catch | ||
( throwM ) | ||
import Crypto.Encoding.BIP39 | ||
( ConsistentEntropy | ||
, DictionaryError (..) | ||
, Entropy | ||
, EntropyError (..) | ||
, EntropySize | ||
, MnemonicSentence | ||
, MnemonicWords | ||
, MnemonicWordsError (..) | ||
, ValidChecksumSize | ||
, ValidEntropySize | ||
, ValidMnemonicSentence | ||
, dictionaryIndexToWord | ||
, entropyRaw | ||
, entropyToWords | ||
, mnemonicPhrase | ||
, mnemonicPhraseToMnemonicSentence | ||
, mnemonicSentenceToListN | ||
, toEntropy | ||
, wordsToEntropy | ||
) | ||
import Data.ByteString | ||
( ByteString ) | ||
import Data.Proxy | ||
( Proxy (..) ) | ||
import Data.Text | ||
( Text ) | ||
import Data.Typeable | ||
( Typeable ) | ||
import GHC.TypeLits | ||
( KnownNat, Nat, natVal ) | ||
|
||
import qualified Basement.Compat.Base as Basement | ||
import qualified Basement.String as Basement | ||
import qualified Crypto.Encoding.BIP39.English as Dictionary | ||
import qualified Crypto.Random.Entropy as Crypto | ||
import qualified Data.Text as T | ||
|
||
-- | A backup-phrase in the form of a non-empty of Mnemonic words | ||
-- Constructor isn't exposed. | ||
data Mnemonic (mw :: Nat) = Mnemonic | ||
{ mnemonicToEntropy :: Entropy (EntropySize mw) | ||
, mnemonicToSentence :: MnemonicSentence mw | ||
} deriving (Eq, Show) | ||
|
||
-- | This is the wrapping of EntropyError of Cardano.Encoding.BIP39 | ||
-- | The EntropyError can be either due to : | ||
-- | (a) invalid entropy length (ErrInvalidEntropyLength) | ||
-- | (b) invalid entropy checksum (ErrInvalidEntropyChecksum) | ||
newtype MnemonicException csz = | ||
UnexpectedEntropyError (EntropyError csz) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does it mean to have an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added the explanation |
||
deriving (Show, Typeable) | ||
|
||
-- | This is the wrapping of errors from Cardano.Encoding.BIP39 | ||
-- | The MnemonicWordsError can be due | ||
-- | to wrong number of words (ErrWrongNumberOfWords) | ||
-- | The EntropyError can be either due to : | ||
-- | (a) invalid entropy length (ErrInvalidEntropyLength) | ||
-- | (b) invalid entropy checksum (ErrInvalidEntropyChecksum) | ||
-- | The DictionaryError can be due to | ||
-- | invalid word (ErrInvalidDictionaryWord) | ||
data MnemonicError csz | ||
= ErrMnemonicWords MnemonicWordsError | ||
| ErrEntropy (EntropyError csz) | ||
| ErrDictionary DictionaryError | ||
deriving (Eq, Show) | ||
|
||
deriving instance Eq (EntropyError czs) | ||
deriving instance Eq MnemonicWordsError | ||
deriving instance Eq DictionaryError | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lots of extra blank lines here! Perhaps just have one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
-- | Smart-constructor for the Entropy | ||
mkEntropy | ||
:: forall n csz. (ValidEntropySize n, ValidChecksumSize n csz) | ||
=> ByteString | ||
-> Either (EntropyError csz) (Entropy n) | ||
mkEntropy = toEntropy | ||
|
||
ambiguousNatVal | ||
:: forall n . (KnownNat n) | ||
=> Integer | ||
ambiguousNatVal = natVal @n Proxy | ||
|
||
-- | Generate Entropy of a given size using a random seed. | ||
-- | ||
-- Example: | ||
-- do | ||
-- ent <- genEntropy :: IO (Entropy 12) | ||
genEntropy | ||
:: forall n csz. (ValidEntropySize n, ValidChecksumSize n csz) | ||
=> IO (Entropy n) | ||
genEntropy = | ||
let | ||
size = | ||
fromIntegral $ ambiguousNatVal @n | ||
eitherToIO = | ||
either (throwM . UnexpectedEntropyError) return | ||
in | ||
(eitherToIO . mkEntropy) =<< Crypto.getEntropy (size `div` 8) | ||
|
||
-- | Smart-constructor for the Mnemonic | ||
mkMnemonic | ||
:: forall mw n csz. | ||
( ConsistentEntropy n mw csz | ||
, EntropySize mw ~ n | ||
) | ||
=> [Text] | ||
-> Either (MnemonicError csz) (Mnemonic mw) | ||
mkMnemonic wordsm = do | ||
phrase <- left ErrMnemonicWords | ||
$ mnemonicPhrase @mw (toUtf8String <$> wordsm) | ||
|
||
sentence <- left ErrDictionary | ||
$ mnemonicPhraseToMnemonicSentence Dictionary.english phrase | ||
|
||
entropy <- left ErrEntropy | ||
$ wordsToEntropy sentence | ||
|
||
pure Mnemonic | ||
{ mnemonicToEntropy = entropy | ||
, mnemonicToSentence = sentence | ||
} | ||
|
||
-- | Convert an Entropy to a corresponding Mnemonic Sentence | ||
entropyToMnemonic | ||
:: forall mw n csz. | ||
( ValidMnemonicSentence mw | ||
, ValidEntropySize n | ||
, ValidChecksumSize n csz | ||
, n ~ EntropySize mw | ||
, mw ~ MnemonicWords n | ||
) | ||
=> Entropy n | ||
-> Mnemonic mw | ||
entropyToMnemonic entropy = Mnemonic | ||
{ mnemonicToSentence = entropyToWords entropy | ||
, mnemonicToEntropy = entropy | ||
} | ||
|
||
-- | Convert 'Entropy' to a raw 'ByteString' | ||
entropyToByteString | ||
:: Entropy n | ||
-> ByteString | ||
entropyToByteString = entropyRaw | ||
|
||
toUtf8String | ||
:: Text | ||
-> Basement.String | ||
toUtf8String = Basement.fromString . T.unpack | ||
|
||
fromUtf8String | ||
:: Basement.String | ||
-> Text | ||
fromUtf8String = T.pack . Basement.toList | ||
|
||
instance (KnownNat csz) => Basement.Exception (MnemonicException csz) | ||
|
||
mnemonicToText | ||
:: Mnemonic mw | ||
-> [Text] | ||
mnemonicToText = | ||
map (fromUtf8String . dictionaryIndexToWord Dictionary.english) | ||
. unListN | ||
. mnemonicSentenceToListN | ||
. mnemonicToSentence |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use explicit imports here 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added explicit imports