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

Add goldens for singleAddressFromKey #433

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/jormungandr/cardano-wallet-jormungandr.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ test-suite unit
-Werror
build-depends:
base
, bech32
, bytestring
, cardano-wallet-core
, cardano-crypto
Expand Down
69 changes: 66 additions & 3 deletions lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/BinarySpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Cardano.Wallet.Jormungandr.BinarySpec (spec) where
Expand All @@ -25,11 +26,18 @@ import Cardano.Wallet.Jormungandr.Binary
, singleAddressFromKey
)
import Cardano.Wallet.Jormungandr.Compatibility
( genesis )
( Jormungandr, genesis )
import Cardano.Wallet.Jormungandr.Environment
( Network (..) )
import Cardano.Wallet.Primitive.Types
( Address (..), Coin (..), Hash (..), SlotId (..), Tx (..), TxOut (..) )
( Address (..)
, Coin (..)
, EncodeAddress (..)
, Hash (..)
, SlotId (..)
, Tx (..)
, TxOut (..)
)
import Control.Exception
( evaluate )
import Data.ByteArray.Encoding
Expand All @@ -44,11 +52,15 @@ import Data.Proxy
( Proxy (..) )
import Data.Quantity
( Quantity (..) )
import Data.Text
( Text )
import Test.Hspec
( Spec, anyErrorCall, describe, it, runIO, shouldBe, shouldThrow )

import qualified Codec.Binary.Bech32 as Bech32
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T

{-# ANN spec ("HLint: ignore Use head" :: String) #-}
spec :: Spec
Expand Down Expand Up @@ -90,8 +102,59 @@ spec = do
it "throws when length (key) != 32" $
evaluate (singleAddressFromKey (Proxy @'Mainnet) (XPub "\148" cc))
`shouldThrow` anyErrorCall
describe "keyToAddress is consistent with jcli" $ do
-- The goldens have been verified with jcli as follows:
-- $ jcli key generate --type=Ed25519 | jcli key to-public
-- ed25519_pk1yv0er3wlzvcauqj470tesdlcwy4dll9w7cvsvn4q30678gh44tnsxdh75c
-- $ jcli address single ed25519_pk1yv0er3wlzvcauqj470tesdlcwy4dll9w7cvsvn4q30678gh44tnsxdh75c --testing
-- ta1sv33lyw9mufnrhsz2hea0xphlpcj4hlu4mmpjpjw5z9ltcaz7k4wwywkd6j
-- $ jcli address single ed25519_pk1yv0er3wlzvcauqj470tesdlcwy4dll9w7cvsvn4q30678gh44tnsxdh75c
-- ca1qv33lyw9mufnrhsz2hea0xphlpcj4hlu4mmpjpjw5z9ltcaz7k4ww0xfchg

bech32KeyToAddrGolden
"ed25519_pk1yv0er3wlzvcauqj470tesdlcwy4dll9w7cvsvn4q30678gh44tnsxdh75c"
"ta1sv33lyw9mufnrhsz2hea0xphlpcj4hlu4mmpjpjw5z9ltcaz7k4wwywkd6j"
"ca1qv33lyw9mufnrhsz2hea0xphlpcj4hlu4mmpjpjw5z9ltcaz7k4ww0xfchg"
Copy link
Member Author

Choose a reason for hiding this comment

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

A caveat is that it is unreadable if you don't know of the meaning of the human readable parts.

A benefit from passing in Text directly is that bech32KeyToAddrGolden has more control, and can output the key in bech32 format despite there being no encodeBech32Key.

Maybe we should add encode/decodeBech32Key in AddressDerivation… 🤔

Copy link
Member

Choose a reason for hiding this comment

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

To make things more readable without impeding too much on the flexibility, you may use a record type and use record fields at the call-site 👍


bech32KeyToAddrGolden
"ed25519_pk1gf85kgfspqzgz9mvmral4e8zatg6d64ep3gk223fla09parjm7zqf4xy3n"
"ta1sdpy7jepxqyqfqghdnv0h7hyut4drfh2hyx9zef298l4u585wt0cgmkm96t"
"ca1qdpy7jepxqyqfqghdnv0h7hyut4drfh2hyx9zef298l4u585wt0cgs7ysh3"

bech32KeyToAddrGolden
"ed25519_pk16cwyfh3jy6ls8ypfpen8pk55d3ufkp9793xc96u32rl83uuv89nsxsnnhg"
"ta1s0tpc3x7xgnt7qus9y8xvux6j3k83xcyhckymqhtj9g0u78n3sukwk7ku9u"
"ca1q0tpc3x7xgnt7qus9y8xvux6j3k83xcyhckymqhtj9g0u78n3sukwakffgx"

bech32KeyToAddrGolden
:: Text
-- ^ public key ("ed25519_pk...")
-> Text
-- ^ corresponding testnet address ("ta...")
-> Text
-- ^ corresponding mainnet address ("ca...")
-> Spec
Copy link
Member Author

Choose a reason for hiding this comment

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

Reasoning for using Text -> Text -> Text:

  • The human readable parts of the bech32 strings already act as types (just not at compile-time)
  • Allows for easy copy-and-paste at call-site for comparison with jcli

bech32KeyToAddrGolden key testnetAddr mainnetAddr = it msg $ do
let xpub = XPub decodeBech32Key cc
encodeAddress (Proxy @(Jormungandr 'Testnet))
(singleAddressFromKey (Proxy @'Testnet) xpub) `shouldBe` testnetAddr
encodeAddress (Proxy @(Jormungandr 'Mainnet))
(singleAddressFromKey (Proxy @'Mainnet) xpub) `shouldBe` mainnetAddr

where
cc = ChainCode "<ChainCode is not used by singleAddressToKey>"
msg = T.unpack key ++ " generates correct testnet and mainnet addresses"
decodeBech32Key :: ByteString
decodeBech32Key =
let
(hrp', dp) = either (error . show) id (Bech32.decodeLenient key)
hrp = T.unpack $ Bech32.humanReadablePartToText hrp'
in
if hrp == "ed25519_pk"
then maybe (error "invalid bech32") id (Bech32.dataPartToBytes dp)
else error $ "failed to decode key: " ++ hrp ++ " is not ed25519_pk"

cc :: ChainCode
cc = ChainCode "<ChainCode shouldn't be used by keyToAddress>"
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

:s #435


genesisHeader :: BlockHeader
genesisHeader = BlockHeader
Expand Down