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

Wallet Id Calculation #156

Merged
merged 2 commits into from
Apr 5, 2019
Merged
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
2 changes: 0 additions & 2 deletions cardano-wallet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ library
, time
, time-units
, transformers
, uuid-types
hs-source-dirs:
src
exposed-modules:
Expand Down Expand Up @@ -131,7 +130,6 @@ test-suite unit
, text
, time-units
, transformers
, uuid-types
, yaml
type:
exitcode-stdio-1.0
Expand Down
11 changes: 8 additions & 3 deletions specifications/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ addressState: &addressState
walletId: &walletId
description: A unique identifier for the wallet
type: string
format: uuid
example: a312e413-e8c8-4bda-9f5b-9b7f518174d8
format: hex
maxLength: 40
minLength: 40
example: 2512a00e9653fe49a44a5886202e24d77eeb998f

walletName: &walletName
type: string
Expand Down Expand Up @@ -557,7 +559,10 @@ parametersWalletId: &parametersWalletId
name: walletId
required: true
type: string
format: uuid
format: hex
maxLength: 40
minLength: 40
example: 2512a00e9653fe49a44a5886202e24d77eeb998f

parametersStakePoolId: &parametersStakePoolId
in: path
Expand Down
4 changes: 2 additions & 2 deletions src/Cardano/Wallet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import Cardano.Wallet.Primitive.AddressDerivation
( ChangeChain (..)
, Passphrase
, deriveAccountPrivateKey
, digest
, generateKeyFromSeed
, publicKey
)
Expand Down Expand Up @@ -127,8 +128,7 @@ mkWalletLayer db network = WalletLayer
{ externalPool = extPool
, internalPool = intPool
}
-- FIXME Compute the wallet id deterministically from the seed
let wid = WalletId (read "00000000-0000-0000-0000-000000000000")
let wid = WalletId (digest $ publicKey rootXPrv)
liftIO (readCheckpoint db (PrimaryKey wid)) >>= \case
Nothing -> do
liftIO $ putCheckpoint db (PrimaryKey wid) wallet
Expand Down
12 changes: 12 additions & 0 deletions src/Cardano/Wallet/Primitive/AddressDerivation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module Cardano.Wallet.Primitive.AddressDerivation
, getIndex
, DerivationType (..)
, publicKey
, digest
, XPub
, XPrv

Expand Down Expand Up @@ -60,6 +61,7 @@ import Cardano.Crypto.Wallet
, deriveXPub
, generateNew
, toXPub
, unXPub
)
import Cardano.Wallet.Binary
( encodeAddress )
Expand All @@ -77,6 +79,8 @@ import Control.Arrow
( left )
import Control.DeepSeq
( NFData )
import Crypto.Hash
( Digest, HashAlgorithm, hash )
import Data.Bifunctor
( first )
import Data.ByteArray
Expand Down Expand Up @@ -176,6 +180,14 @@ publicKey
publicKey (Key xprv) =
Key (toXPub xprv)

-- | Hash a public key to some other representation.
digest
:: HashAlgorithm a
=> Key level XPub
-> Digest a
digest (Key xpub) =
hash (unXPub xpub)

{-------------------------------------------------------------------------------
Passphrases
-------------------------------------------------------------------------------}
Expand Down
21 changes: 12 additions & 9 deletions src/Cardano/Wallet/Primitive/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ import Prelude

import Control.DeepSeq
( NFData (..) )
import Crypto.Hash
( Blake2b_160, Digest, digestFromByteString )
import Data.ByteArray.Encoding
( Base (Base16), convertToBase )
( Base (Base16), convertFromBase, convertToBase )
import Data.ByteString
( ByteString )
import Data.ByteString.Base58
Expand All @@ -103,8 +105,6 @@ import Data.Text.Class
( FromText (..), TextDecodingError (..), ToText (..) )
import Data.Time
( UTCTime )
import Data.UUID.Types
( UUID )
import Data.Word
( Word16, Word32, Word64 )
import Fmt
Expand All @@ -131,7 +131,6 @@ import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.UUID.Types as UUID


{-------------------------------------------------------------------------------
Expand Down Expand Up @@ -176,17 +175,21 @@ walletNameMinLength = 1
walletNameMaxLength :: Int
walletNameMaxLength = 255

newtype WalletId = WalletId { getWalletId :: UUID }
newtype WalletId = WalletId { getWalletId :: Digest Blake2b_160 }
deriving (Generic, Eq, Ord, Show)

instance FromText WalletId where
fromText = maybe
(Left $ TextDecodingError "A wallet ID must be a valid UUID")
fromText txt = maybe
(Left $ TextDecodingError msg)
(Right . WalletId)
. UUID.fromText
(decodeHex txt >>= digestFromByteString @_ @ByteString)
where
msg = "wallet id should be an hex-encoded string of 40 characters"
decodeHex =
either (const Nothing) Just . convertFromBase Base16 . T.encodeUtf8

instance ToText WalletId where
toText = UUID.toText . getWalletId
toText = T.decodeUtf8 . convertToBase Base16 . getWalletId

data WalletState
= Ready
Expand Down
10 changes: 5 additions & 5 deletions test/data/Cardano/Wallet/Api/ApiT WalletId.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"seed": 8206571283525959853,
"seed": -3501594004796652497,
"samples": [
"00006131-0000-527b-0000-544d00001a6d",
"00000133-0000-0c79-0000-007c00002a7f",
"000059e1-0000-19f3-0000-74860000330c",
"00001a52-0000-7443-0000-5981000063ee"
"4fbba8356b67d2e6b1e6f4dc9d35e507eb5f5398",
"119870a75a251c18ca8726c74a029cad4baf72bf",
"cad45b3ca86421f01212cd4bec4fc3d91f209d3e",
"0f881e3e86192aa290af0500503b3038559a8d4c"
]
}
78 changes: 42 additions & 36 deletions test/data/Cardano/Wallet/Api/ApiWallet.json
Original file line number Diff line number Diff line change
@@ -1,110 +1,116 @@
{
"seed": 1080154664973591237,
"seed": -7193288254086185690,
"samples": [
{
"passphrase": {
"last_updated_at": "1864-04-24T03:49:30.165952219949Z"
"last_updated_at": "1864-06-06T17:04:22.128113480808Z"
Copy link
Contributor

Choose a reason for hiding this comment

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

why here year 1864?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is arbitrarily generated 🤷‍♂️

},
"address_pool_gap": 82,
"address_pool_gap": 16,
"state": {
"status": "ready"
"status": "restoring",
"progress": {
"quantity": 18,
"unit": "percent"
}
},
"balance": {
"total": {
"quantity": 105,
"quantity": 138,
"unit": "lovelace"
},
"available": {
"quantity": 50,
"quantity": 16,
"unit": "lovelace"
}
},
"name": "Zncqw2i6*@0:'; T𑫧𥣊xz;4dkOG-]M$$𩭤n,3+tv9uXKR[aH@sbDa0&7a,9z,/G{CX墣/DOCpx[<u#!+ukAIh{EPx1lZO4pI|nBT/.HYzo[g ?!\"^V/q\\;V,OOAO&3i|Q\\zu{5K_{2}?UBdbh+O 4/(_QT\"#8/yM:ZJ54fjq`DSJ3R1i(`xR<8tL(e",
"name": "^^q;a%R,\"Y&HMCo;7.sn,E`D?IG]VE{opL6VZ;>S, J`}",
"delegation": {
"status": "not_delegating"
},
"id": "00007114-0000-5932-0000-79b000000601"
"id": "2512a00e9653fe49a44a5886202e24d77eeb998f"
},
{
"passphrase": {
"last_updated_at": "1864-05-03T12:25:30.142439976822Z"
"last_updated_at": "1864-04-19T12:31:11.122808393149Z"
},
"address_pool_gap": 11,
"address_pool_gap": 60,
"state": {
"status": "ready"
"status": "restoring",
"progress": {
"quantity": 86,
"unit": "percent"
}
},
"balance": {
"total": {
"quantity": 241,
"quantity": 148,
"unit": "lovelace"
},
"available": {
"quantity": 49,
"quantity": 125,
"unit": "lovelace"
}
},
"name": "drU<f[U#E]B5Q3M\\-0&dD`9|hluP3`/yaRo8𧊷hzr3u4$U8Us1=\\3f!aS@}Cb4yK~𩲡@#WAlgmi/5&XXC0[[q}Spo*|`S,_:v-FBq5E2]5A-4 c<}G^ L9l𤹒sl9-[5lhf6hn}c5D>h?T><kXH3zdwFSZnfG;$pNZPS?w% }BewiHsjD,)H7tTf49Om}}<l𩩒~i2NK(\\#|+%7r^H73MDo^)B^h}(Mhd\"%FM0^_㯈}#':z{,{joC4𤒺2cq5𢧾𠳔:<)<(",
"name": "GI2wi\\Ax&2Sn&jAf2'Crd8𣼎FtPU[=$zq?s+eJW;=bBSdC`S-zW𡂶F.K|e_o!+<\"n6n3v|:~~\"!l:n\\SQ(k@}o@HaOx:>?∴Ap3]@M,i!E🔱{EEf-N(4KkUh2Hl`6=+uu=g=c>D' p>'o4vsfr>iFc;#oew).h@QA$p揣'X.㸣L7=kz@X=U@={/𒉸pdX`pElMI~4𠍀0QXxDp;vH`)xYd49|3",
"delegation": {
"status": "not_delegating"
"status": "delegating",
"target": "Ax*"
},
"id": "00000ef6-0000-7578-0000-552500004dfc"
"id": "dfdc46a67c241b69302b6f46797155e99a4be3bf"
},
{
"passphrase": {
"last_updated_at": "1864-05-02T17:32:51.530279907507Z"
"last_updated_at": "1864-05-19T22:42:57.694847558115Z"
},
"address_pool_gap": 78,
"address_pool_gap": 61,
"state": {
"status": "restoring",
"progress": {
"quantity": 78,
"quantity": 33,
"unit": "percent"
}
},
"balance": {
"total": {
"quantity": 163,
"quantity": 113,
"unit": "lovelace"
},
"available": {
"quantity": 210,
"quantity": 141,
"unit": "lovelace"
}
},
"name": "vA$r㴥8YFGV;s6vRCbk~Qg.5fM8𨲒$e>4a=0j+1@Sp𐔛1_=uE𐎉aDFZvu",
"name": "U\\껖\\0\"w9[4Rws#rc:3𣈪DKjT}]𦜠`n[5\\/~'RO)q!1956ek𦬗DsN s;wllTT5o全w?.&SP+9<pjy}s%U/Y2eu-#!W TuZ,d08 7D({=Ww+EvN\\𠼕/鰁I^nAeNm帚g/NH\"pc",
"delegation": {
"status": "not_delegating"
"status": "delegating",
"target": "[!)"
},
"id": "00004bfe-0000-4747-0000-0b6f00007616"
"id": "8f1fd5e6a3aee198b9a811c7e28e33bee5321c2a"
},
{
"passphrase": {
"last_updated_at": "1864-05-19T19:55:26.164488147172Z"
"last_updated_at": "1864-06-06T23:57:20.172560053324Z"
},
"address_pool_gap": 92,
"address_pool_gap": 55,
"state": {
"status": "restoring",
"progress": {
"quantity": 6,
"unit": "percent"
}
"status": "ready"
},
"balance": {
"total": {
"quantity": 159,
"quantity": 6,
"unit": "lovelace"
},
"available": {
"quantity": 89,
"quantity": 73,
"unit": "lovelace"
}
},
"name": "TnbC🙣*11f$E/ke:`x<0u/\"䬱RWiU|*Xa-$-l<nZ:=^N9\\hn/rSlZhXr+D/MhFi7)%\"OeYl}hOSU@&<HqZjD*>%m1lYbl)'jV'aᯭ;fH*XHBL<nKO𥔭-g\\-8}DpTd_c`Z:A^X%()rl`_d2$E",
"name": "0䗼=P;I6R`5M2z",
"delegation": {
"status": "delegating",
"target": "'4,"
"target": "2r6"
},
"id": "00002008-0000-008a-0000-1a7100002145"
"id": "8ed0b58003abc9109c76328fa7e0fd1876c58d20"
}
]
}
Copy link
Member Author

Choose a reason for hiding this comment

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

re-generated golden tests

13 changes: 7 additions & 6 deletions test/unit/Cardano/Wallet/Api/TypesSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import Control.Lens
( Lens', at, (^.) )
import Control.Monad
( replicateM )
import Crypto.Hash
( hash )
import Data.Aeson
( FromJSON (..), ToJSON (..) )
import Data.Aeson.QQ
Expand Down Expand Up @@ -96,7 +98,7 @@ import Data.Swagger.Declare
import Data.Typeable
( Typeable )
import Data.Word
( Word32, Word8 )
( Word8 )
import GHC.TypeLits
( KnownSymbol, symbolVal )
import Numeric.Natural
Expand Down Expand Up @@ -132,10 +134,10 @@ import Test.QuickCheck.Instances.Time

import qualified Data.Aeson.Types as Aeson
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as B8
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.UUID.Types as UUID
import qualified Data.Yaml as Yaml
import qualified Prelude

Expand Down Expand Up @@ -303,10 +305,9 @@ instance Arbitrary PoolId where
arbitrary = PoolId . T.pack <$> replicateM 3 arbitraryPrintableChar

instance Arbitrary WalletId where
arbitrary = WalletId . uuidFromWords <$> arbitrary

uuidFromWords :: (Word32, Word32, Word32, Word32) -> UUID.UUID
uuidFromWords (a, b, c, d) = UUID.fromWords a b c d
arbitrary = do
bytes <- BS.pack <$> replicateM 16 arbitrary
Copy link
Contributor

Choose a reason for hiding this comment

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

why 16 here, because address pool gap is 16?

Copy link
Member Author

Choose a reason for hiding this comment

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

No. Just arbitrary.

return $ WalletId (hash bytes)

instance Arbitrary WalletName where
arbitrary = do
Expand Down
7 changes: 4 additions & 3 deletions test/unit/Cardano/Wallet/DB/MVarSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import Control.Monad.IO.Class
( liftIO )
import Control.Monad.Trans.Except
( runExceptT )
import Crypto.Hash
( hash )
import Data.Map.Strict
( Map )
import Data.Quantity
Expand Down Expand Up @@ -71,7 +73,6 @@ import qualified Data.ByteString.Char8 as B8
import qualified Data.List as L
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Data.UUID.Types as UUID


spec :: Spec
Expand Down Expand Up @@ -227,8 +228,8 @@ deriving instance Show (PrimaryKey WalletId)
instance Arbitrary (PrimaryKey WalletId) where
shrink _ = []
arbitrary = do
k <- choose (0, 10)
return $ PrimaryKey $ WalletId $ UUID.fromWords k 0 0 0
bytes <- B8.pack . pure <$> elements ['a'..'k']
return $ PrimaryKey $ WalletId $ hash bytes
Copy link
Member Author

Choose a reason for hiding this comment

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

So here, we try to keep the generator's entropy quite low because we do want to generate conflicts in the properties.


instance Arbitrary (Hash "Tx") where
shrink _ = []
Expand Down
Loading