Skip to content
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

Merged
merged 1 commit into from
Mar 13, 2019
Merged

Port mnemonic module #47

merged 1 commit into from
Mar 13, 2019

Conversation

paweljakubas
Copy link
Contributor

@paweljakubas paweljakubas commented Mar 11, 2019

Issue Number

#16

Overview

  • I have removed mnemonicToSeed, mnemonicToAesKey, blake2b from main module and adapted tests accordingly
  • I have stopped support of Mnemonic to Buildable and ToSchema
  • I have added executable in Cardano/Wallet/Mnemonic
  • I have used Mnemonic 15 rather than Mnemonic 12 as default in executable and specMnemonic used in forbidden

Now it works like that:

$ stack build
[pawel@arch cardano-wallet]$ stack exec cardano-generate-mnemonic
["love","dutch","usual","uphold","whale","certain","merry","horror","bulb","injury","school","crane","grass","police","beauty"]

Comments

  1. The module depends on cardano-crypto
  2. I added (AllowAmbiguousTypes needed):
126     :: forall n . (KnownNat n)                                                                                                                                                            
127     => Integer                                                                                                                                                                            
128 ambiguousNatVal = natVal @n Proxy
  1. Now eitherToParse looks like below (relying on Show rather than Buildable):
328 eitherToParser                                                                                                                                                                            
329     :: Show a                                                                                                                                                                             
330     => Either a b                                                                                                                                                                         
331     -> Parser b                                                                                                                                                                           
332 eitherToParser =                                                                                                                                                                          
333     either (fail . show) pure
  1. Prelude is used.

deriving (Show)



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of extra blank lines here! Perhaps just have one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

:: Basement.String
-> Text
fromUtf8String =
pack . Basement.toList
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing functions this way is going to use a lot of vertical space. While it's definitely good to wrap once the 80 character limit is exceeded, I think it's not required here.

Perhaps write the above pair of functions like this:

toUtf8String :: Text -> Basement.String
toUtf8String = Basement.fromString . unpack

fromUtf8String :: Basement.String -> Text
fromUtf8String = pack . Basement.toList

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sticked to the types convention , but readjusted definition as requested

Basement.toBytes Basement.UTF8 . mnemonicSentenceToString Dictionary.english



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to leave so much vertical space between functions. One blank line is really enough! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


module Cardano.Wallet.Mnemonic
(
-- * Types
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid variable length indentation here, and use a fixed-width indentation here (4 characters).

https://github.com/input-output-hk/cardano-wallet/wiki/Coding-Standards#avoid-variable-length-indentation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, let's indent module exports by 4 chars only 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


import Prelude


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blank line required here. From the coding guidelines.

in order to maximise readability, imports should be grouped into two groups, separated by a blank newline.

  • Explicit imports
  • Qualified imports

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the point. import Prelude is the only implicit import why it is standing separate. Then, explicit and quantified related groups go

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see in every src file this convention


-- | The initial seed has to be vector or length multiple of 4 bytes and shorter
-- than 64 bytes. Not that this is good for testing or examples, but probably
-- not for generating truly random Mnemonic words.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small typo? Perhaps this should be:

"Note that this is good for testing or examples, but probably not for generating truly random Mnemonic words."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected



newtype MnemonicException csz =
UnexpectedEntropyError (EntropyError csz)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean to have an UnexpectedEntropyError? Perhaps add a comment here with an explanation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the explanation

= ErrMnemonicWords MnemonicWordsError
| ErrEntropy (EntropyError csz)
| ErrDictionary DictionaryError
| ErrForbidden
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do these errors mean? I recommend adding some comments here! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the explanation

, cborg
, containers
, cryptonite
, data-default
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not, we don't need this. A plain defaultWhatever function is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed data-default

@@ -47,6 +53,7 @@ library
, text
, time-units
, transformers
, QuickCheck
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No QuickCheck / Arbitrary instances in the source please 🙏
Those only makes sense during testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected

, cardano-wallet
, aeson
hs-source-dirs:
src/Cardano/Wallet/Mnemonic
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All executables should be located in app/<executable-sub-folder>. Let's try to stick with that 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the executable to app/mnemonic

Copy link
Member

@KtorZ KtorZ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for porting this 👍
A few minor adjustments to make sure it fits with our current style and that we don't introduce some dependencies.


module Cardano.Wallet.Mnemonic
(
-- * Types
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, let's indent module exports by 4 chars only 👍

[ mnemonicToSentence (def @(Mnemonic 12))
]
in
any (constEq bytes) forbiddenMnemonics
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also get rid of this one and make sure (as it's now in the spec) that the mnemonics from the documentation represent invalid mnemonics (so that we don't have to have an extra hard-coded mnemonic here..

Copy link
Contributor Author

@paweljakubas paweljakubas Mar 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the api spec we have (Mnemonic 15) - you mean - put the exact example from the spec and correct the affected tests, right? I assume so (please correct me if I am wrong)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, not check for forbidden mnemonics. They won't be needed if the mnemonic we put in the documentation is invalid (so by essence, it can't be used) such that we can remove all this logic from our way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, added respective test to make sure mnemonic example from API is invalid:

114         it "Mnemonic from Api is invalid" $ do                                                                                                                                            
115             let mnemonicFromApi =                                                                                                                                                         
116                     "[squirrel,material,silly,twice,direct,slush,pistol,razor,become,junk,kingdom,flee,squirrel,silly,twice]"                                                             
117             (mkMnemonic @15 . extractWords) mnemonicFromApi `shouldSatisfy` isLeft

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In detail it gives:

Left (ErrEntropy (ErrInvalidEntropyChecksum (Checksum 25) (Checksum 26)))

import Data.Proxy
( Proxy (..) )
import Data.Text
( Text, pack, unpack )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pack and unpack are ambiguous (same name in all ByteString packages), so let's use qualified imports for them 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

( when, (>=>) )
import Control.Monad.Catch
( throwM )
import Crypto.Encoding.BIP39
Copy link
Member

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 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added explicit imports

main = do
backupPhrase <- generateBackupPhrase
let backupPhraseString = backupPhraseToString backupPhrase
putStrLn backupPhraseString
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor note: would be better to actually output a Text or a ByteString here. In practice, we probably never wants to output strings. They are inefficient (list of chars) and don't play well in multi-thread contexts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used Text

. map (fromUtf8String . dictionaryIndexToWord Dictionary.english)
. unListN
. mnemonicSentenceToListN
. mnemonicToSentence
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest not to bring any JSON dependencies at this stage and provide instead, serialization combinators that may be use to define JSON instances where it's relevant.

mnemonicToText :: Mnemonic mw -> [Text]
mnemonicToText = ...

-- or

unMnemonic :: Mnemonic mw -> [Text]
unMnemonic = ... 

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added mnemonicToText because used in executable. But left aeson as they are used in the tests

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, the Aeson tests should also be removed 👍
They will probably be part of the API tests, but that's the responsibility of the API layer, not this module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aeson tests and ToJSON, FromJSON instances removed

( length )


import Cardano.Wallet.Mnemonic
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit imports here 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done



import Prelude hiding
( length )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not hide length, but rather use a qualified import on Data.ByteString for that 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

putStrLn backupPhraseString

backupPhraseToString :: Mnemonic 12 -> String
backupPhraseToString backupPhrase = show $ encode backupPhrase
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The show, instead of unpack, is a change in behaviour.

stack exec cardano-generate-mnemonic
"[\"gaze\",\"envelope\",\"submit\",\"umbrella\",\"diesel\",\"spirit\",\"twelve\",\"tonight\",\"arena\",\"salute\",\"gallery\",\"seminar\"]"

vs

cardano-generate-mnemonic
["moral","glass","grain","sure","weather","awful","scorpion","electric","vanish","injury","few","shell"]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now it is ok:

[pawel@arch cardano-wallet]$ stack exec cardano-generate-mnemonic
["grant","fan","matter","receive","shy","flash","buyer","worth","crater","interest","noble","pink","nurse","budget","pave"]

@paweljakubas paweljakubas force-pushed the paweljakubas/16/port-mnemonic-module branch from 2ec79d3 to 0f88116 Compare March 12, 2019 15:48
@paweljakubas
Copy link
Contributor Author

Complied with all remarks (hope so), rebased and squashed

@paweljakubas paweljakubas force-pushed the paweljakubas/16/port-mnemonic-module branch from 0f88116 to dee07aa Compare March 13, 2019 05:20
[16] mnemonic module and unit tests working properly

[16] add mnemonic executable

[16] weeder and hlint fixes

[16] one more hlint suggestion

[16] move exec to app directory

[16] Review changes

[16] Removing aeson and resigning from forbiddenMnemonics
@paweljakubas
Copy link
Contributor Author

Removed Aeson instances and forbidenMnemonics from Cardano.Wallet.Mnemonic. Refatored tests and added new one checking if mnemonic from API is really invalid.

Code rebased and squashed.

@paweljakubas paweljakubas requested a review from KtorZ March 13, 2019 05:43
@KtorZ KtorZ merged commit 5757ccc into master Mar 13, 2019
@KtorZ KtorZ deleted the paweljakubas/16/port-mnemonic-module branch March 13, 2019 06:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants