Skip to content
Permalink
Browse files

extend RndState by adding ChangeState

  • Loading branch information...
paweljakubas committed Aug 13, 2019
1 parent 67907ab commit a9e69c2564edf191ad3cc11e81e7eff076782b13
@@ -906,7 +906,9 @@ instance PersistState W.RndState where
deleteState _ = pure ()

-- Construct a 'RndState' from the root private key
selectState (wid, _) = fmap (W.RndState . fst) <$> selectPrivateKey wid
selectState (wid, _) = do
let constructRndState key = W.RndState key W.emptyChangeState
fmap (constructRndState . fst) <$> selectPrivateKey wid

{-------------------------------------------------------------------------------
Logging
@@ -18,14 +18,17 @@ module Cardano.Wallet.Primitive.AddressDiscovery.Random
(
-- ** State
RndState (..)
, emptyChangeState
, updateChangeState
, ChangeState (..)
) where

import Prelude

import Cardano.Byron.Codec.Cbor
( decodeAddressDerivationPath, decodeAddressPayload, deserialiseCbor )
import Cardano.Wallet.Primitive.AddressDerivation
( Depth (..), DerivationType (..), Index, XPrv )
( Depth (..), DerivationType (..), Index (..), XPrv )
import Cardano.Wallet.Primitive.AddressDerivation.Random
( RndKey (..), deriveAccountPrivateKey, deriveAddressPrivateKey )
import Cardano.Wallet.Primitive.AddressDiscovery
@@ -43,23 +46,29 @@ import Control.Monad
( join )
import Data.Maybe
( isJust )
import Data.Set
( Set )
import GHC.Generics
( Generic )

newtype RndState = RndState { getRndState :: RndKey 'RootK XPrv }
deriving (Generic)
import qualified Data.Set as Set

data RndState = RndState
{ rndKey :: RndKey 'RootK XPrv
, changeState :: ChangeState
} deriving (Generic)

instance NFData RndState

-- An address is considered to belong to the 'RndState' wallet if it can be decoded
-- as a Byron HD random address, and where the wallet key can be used to decrypt
-- the address derivation path.
instance IsOurs RndState where
isOurs addr st@(RndState key) =
isOurs addr st@(RndState key _) =
(isJust $ addressToPath addr key, st)

instance IsOwned RndState RndKey where
isOwned (RndState key) (_,pwd) addr =
isOwned (RndState key _) (_,pwd) addr =
case addressToPath addr key of
Just (accIx, addrIx) -> do
let accXPrv = deriveAccountPrivateKey pwd key accIx
@@ -76,9 +85,36 @@ addressToPath (Address addr) key = do
payload <- deserialiseCbor decodeAddressPayload addr
join $ deserialiseCbor (decodeAddressDerivationPath pwd) payload

-- | Change state keeps track of the first free index to use and
-- already used addresses (not only change addresses but ever known, ie. generated)
data ChangeState = ChangeState
{ forbidenAddresses :: Set Address
, nextIndex :: (Index 'Soft 'AccountK, Index 'Soft 'AddressK)
} deriving (Generic, Show, Eq)

instance NFData ChangeState

emptyChangeState :: ChangeState
emptyChangeState = ChangeState Set.empty (minBound, minBound)

updateChangeState
:: Address
-> ChangeState
-> ChangeState
updateChangeState addr (ChangeState addrs (Index accIx, Index addrIx)) =
let (accIx', addrIx') =
if (addrIx == maxBound && accIx == maxBound) then
(minBound, minBound)
else if (addrIx == maxBound) then
(accIx + 1, minBound)
else (accIx, addrIx + 1)
in ChangeState (Set.insert addr addrs) (Index accIx', Index addrIx')


instance GenChange RndState where
genChange s = (error "GenChange RndState unimplemented", s)


-- Unlike sequential derivation, we can't derive an order from the index only
-- (they are randomly generated), nor anything else in the address itself.
--
@@ -36,7 +36,7 @@ import Cardano.Wallet.Primitive.AddressDerivationSpec
import Cardano.Wallet.Primitive.AddressDiscovery
( IsOurs (..), IsOwned (..) )
import Cardano.Wallet.Primitive.AddressDiscovery.Random
( RndState (..) )
( RndState (..), emptyChangeState )
import Cardano.Wallet.Primitive.Types
( Address (..) )
import Data.ByteArray.Encoding
@@ -197,7 +197,7 @@ checkIsOwned GoldenTest{..} = do
where
pwd = Passphrase ""
Right addr' = Address <$> convertFromBase Base16 addr
rndState@(RndState rootXPrv) = rndStateFromMnem arbitraryMnemonic
rndState@(RndState rootXPrv _) = rndStateFromMnem arbitraryMnemonic
accXPrv = deriveAccountPrivateKey pwd rootXPrv (Index accIndex)
addrXPrv = deriveAddressPrivateKey pwd accXPrv (Index addrIndex)
expectation = if expected then
@@ -206,7 +206,7 @@ checkIsOwned GoldenTest{..} = do


rndStateFromMnem :: [Text] -> RndState
rndStateFromMnem mnem = RndState rootXPrv
rndStateFromMnem mnem = RndState rootXPrv emptyChangeState
where
rootXPrv = generateKeyFromSeed (Passphrase seed) (Passphrase "")
Right (Passphrase seed) = fromMnemonic @'[12] mnem
@@ -234,7 +234,7 @@ prop_derivedKeysAreOurs
accIx addrIx =
fst (isOurs addr st) .&&. not (fst (isOurs addr st'))
where
rk = getRndState st
rk = rndKey st
addr = keyToAddress @DummyTarget addrKey
accKey = deriveAccountPrivateKey pwd rk accIx
addrKey = publicKey $ deriveAddressPrivateKey pwd accKey addrIx
@@ -249,12 +249,12 @@ prop_derivedKeysAreOwned
(RndStatePassphrase st pwd)
(RndStatePassphrase st' pwd')
accIx addrIx =
isOwned st (getRndState st, pwd) addr === Just (addrKeyPrv, pwd)
isOwned st (rndKey st, pwd) addr === Just (addrKeyPrv, pwd)
.&&.
isOwned st' (getRndState st', pwd') addr === Nothing
isOwned st' (rndKey st', pwd') addr === Nothing
where
addr = keyToAddress @DummyTarget (publicKey addrKeyPrv)
accKey = deriveAccountPrivateKey pwd (getRndState st) accIx
accKey = deriveAccountPrivateKey pwd (rndKey st) accIx
addrKeyPrv = deriveAddressPrivateKey pwd accKey addrIx


@@ -263,10 +263,10 @@ prop_derivedKeysAreOwned
-------------------------------------------------------------------------------}

instance Eq RndState where
(RndState a) == (RndState b) = getKey a == getKey b
(RndState a _) == (RndState b _) = getKey a == getKey b

instance Show RndState where
show (RndState a) = show (getKey a)
show (RndState a _) = show (getKey a)

data RndStatePassphrase = RndStatePassphrase
{ rndState :: RndState
@@ -280,7 +280,7 @@ instance Arbitrary RndStatePassphrase where
<$> genPassphrase @"seed" (16, 32)
<*> genPassphrase @"encryption" (0, 16)
let st = generateKeyFromSeed s e
pure $ RndStatePassphrase (RndState st) e
pure $ RndStatePassphrase (RndState st emptyChangeState) e
where
genPassphrase :: (Int, Int) -> Gen (Passphrase purpose)
genPassphrase range = do
@@ -43,7 +43,7 @@ import Cardano.Wallet.Primitive.AddressDerivation.Random
import Cardano.Wallet.Primitive.AddressDiscovery
( IsOurs (..) )
import Cardano.Wallet.Primitive.AddressDiscovery.Random
( RndState (..) )
( RndState (..), emptyChangeState )
import Cardano.Wallet.Primitive.Types
( Address (..)
, Coin (..)
@@ -192,22 +192,24 @@ prop_derivedKeysAreOurs
-> RndKey 'RootK XPrv
-> Property
prop_derivedKeysAreOurs seed encPwd accIx addrIx rk' =
isOurs addr (RndState rootXPrv) === (True, RndState rootXPrv) .&&.
isOurs addr (RndState rk') === (False, RndState rk')
isOurs addr (RndState rootXPrv c) === (True, RndState rootXPrv c) .&&.
isOurs addr (RndState rk' c) === (False, RndState rk' c)
where
rndKey = publicKey $ unsafeGenerateKeyFromSeed (accIx, addrIx) seed encPwd
c = emptyChangeState
key = publicKey $ unsafeGenerateKeyFromSeed (accIx, addrIx) seed encPwd
rootXPrv = generateKeyFromSeed seed encPwd
addr = keyToAddress @(HttpBridge n) rndKey
addr = keyToAddress @(HttpBridge n) key

{-------------------------------------------------------------------------------
Arbitrary instances
-------------------------------------------------------------------------------}

instance Eq RndState where
(RndState a) == (RndState b) = getKey a == getKey b
(RndState k1 c1) == (RndState k2 c2) =
getKey k1 == getKey k2 && c1 == c2

instance Show RndState where
show (RndState a) = show (getKey a)
show (RndState a _) = show (getKey a)

instance Show XPrv where
show = show . unXPrv

0 comments on commit a9e69c2

Please sign in to comment.
You can’t perform that action at this time.