Skip to content

Commit

Permalink
define a 'SeqAnyState' analogous to the 'RndAnyState' for benchmarks
Browse files Browse the repository at this point in the history
  This make sure that benchmarks reflects a real-setup with a bit more fidelity by also storing and retrieving the address space.
  • Loading branch information
KtorZ committed Aug 27, 2020
1 parent 02aba16 commit ce3f2a4
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 3 deletions.
6 changes: 6 additions & 0 deletions lib/core/src/Cardano/Wallet/DB/Sqlite.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1481,6 +1481,12 @@ class PersistState s where
Sequential address discovery
-------------------------------------------------------------------------------}

-- piggy-back on SeqState existing instance, to simulate the same behavior.
instance PersistState (Seq.SeqState n k) => PersistState (Seq.SeqAnyState n k p)
where
insertState (wid, sl) = insertState (wid, sl) . Seq.innerState
selectState (wid, sl) = fmap Seq.SeqAnyState <$> selectState (wid, sl)

instance
( Eq (k 'AccountK XPub)
, PersistPublicKey (k 'AccountK)
Expand Down
118 changes: 117 additions & 1 deletion lib/core/src/Cardano/Wallet/Primitive/AddressDiscovery/Sequential.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ module Cardano.Wallet.Primitive.AddressDiscovery.Sequential
, SeqState (..)
, mkSeqStateFromRootXPrv
, mkSeqStateFromAccountXPub

-- ** Benchmarking
, SeqAnyState (..)
, mkSeqAnyState
) where

import Prelude
Expand Down Expand Up @@ -87,7 +91,7 @@ import Cardano.Wallet.Primitive.AddressDiscovery
, KnownAddresses (..)
)
import Cardano.Wallet.Primitive.Types
( Address, AddressState (..), invariant )
( Address (..), AddressState (..), ChimericAccount (..), invariant )
import Control.Applicative
( (<|>) )
import Control.DeepSeq
Expand All @@ -96,6 +100,8 @@ import Control.Monad
( unless )
import Data.Bifunctor
( first )
import Data.Digest.CRC32
( crc32 )
import Data.Function
( (&) )
import Data.Map.Strict
Expand All @@ -118,6 +124,8 @@ import GHC.Generics
( Generic )
import GHC.Stack
( HasCallStack )
import GHC.TypeLits
( KnownNat, Nat, natVal )

import qualified Data.List as L
import qualified Data.Map.Strict as Map
Expand Down Expand Up @@ -718,3 +726,111 @@ instance
addresses (liftPaymentAddress @n @k) (externalPool s)
in
nonChangeAddresses <> changeAddresses

--------------------------------------------------------------------------------
--
-- SeqAnyState
--
-- For benchmarking and testing arbitrary large sequential wallets.

-- | An "unsound" alternative that can be used for benchmarking and stress
-- testing. It re-uses the same underlying structure as the `SeqState` but
-- it discover addresses based on an arbitrary ratio instead of decrypting the
-- derivation path.
--
-- The proportion is stored as a type-level parameter so that we don't have to
-- alter the database schema to store it. It simply exists and depends on the
-- caller creating the wallet to define it.
newtype SeqAnyState (network :: NetworkDiscriminant) key (p :: Nat) = SeqAnyState
{ innerState :: SeqState network key
} deriving (Generic)

deriving instance
( Show (k 'AccountK XPub)
, Show (k 'AddressK XPub)
, Show (KeyFingerprint "payment" k)
) => Show (SeqAnyState n k p)

instance
( NFData (k 'AccountK XPub)
, NFData (k 'AddressK XPub)
, NFData (KeyFingerprint "payment" k)
)
=> NFData (SeqAnyState n k p)

-- | Initialize the HD random address discovery state from a root key and RNG
-- seed.
--
-- The first argument is expected to be a ratio (between 0 and 1) of addresses
-- we ought to simply recognize as ours. So, giving .5 means that 50% of the
-- entire address space of the network will be considered ours, picked randomly.
mkSeqAnyState
:: forall (p :: Nat) n k.
( SoftDerivation k
, MkKeyFingerprint k (Proxy n, k 'AddressK XPub)
, MkKeyFingerprint k Address
, WalletKey k
, Bounded (Index (AddressIndexDerivationType k) 'AddressK)
)
=> (k 'RootK XPrv, Passphrase "encryption")
-> AddressPoolGap
-> SeqAnyState n k p
mkSeqAnyState credentials poolGap = SeqAnyState
{ innerState = mkSeqStateFromRootXPrv credentials poolGap
}

instance
( SoftDerivation k
, MkKeyFingerprint k (Proxy n, k 'AddressK XPub)
, KnownNat p
) => IsOurs (SeqAnyState n k p) Address
where
isOurs (Address bytes) st@(SeqAnyState inner)
| crc32 bytes < p =
let
edge = Map.size (indexedKeys $ externalPool inner)
ix = toEnum (edge - fromEnum (gap $ externalPool inner))
pool' = extendAddressPool @n ix (externalPool inner)
in
(True, SeqAnyState (inner { externalPool = pool' }))
| otherwise =
(False, st)
where
p = floor (double (maxBound :: Word32) * double (natVal (Proxy @p)) / 100)

double :: Integral a => a -> Double
double = fromIntegral

instance IsOurs (SeqAnyState n k p) ChimericAccount
where
isOurs _account state = (False, state)

instance
( SoftDerivation k
, MkKeyFingerprint k (Proxy n, k 'AddressK XPub)
, AddressIndexDerivationType k ~ 'Soft
, KnownNat p
) => IsOwned (SeqAnyState n k p) k
where
isOwned _ _ _ = Nothing

instance
( SoftDerivation k
) => GenChange (SeqAnyState n k p)
where
type ArgGenChange (SeqAnyState n k p) = ArgGenChange (SeqState n k)
genChange a (SeqAnyState s) = SeqAnyState <$> genChange a s

instance
( MkKeyFingerprint k (Proxy n, k 'AddressK XPub)
, MkKeyFingerprint k Address
, SoftDerivation k
) => CompareDiscovery (SeqAnyState n k p)
where
compareDiscovery (SeqAnyState s) = compareDiscovery s

instance
( PaymentAddress n k
) => KnownAddresses (SeqAnyState n k p)
where
knownAddresses (SeqAnyState s) = knownAddresses s
23 changes: 21 additions & 2 deletions lib/shelley/bench/Restore.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import Cardano.Wallet.Primitive.AddressDiscovery.Random
( mkRndAnyState, mkRndState )
import Cardano.Wallet.Primitive.AddressDiscovery.Sequential
( AddressPoolGap
, SeqAnyState (..)
, SeqState (..)
, mkAddressPoolGap
, mkSeqAnyState
Expand Down Expand Up @@ -151,6 +152,8 @@ import Fmt
( Buildable, build, fmt, genericF, pretty, (+|), (+||), (|+), (||+) )
import GHC.Generics
( Generic )
import GHC.TypeLits
( Nat )
import Say
( sayErr )
import System.FilePath
Expand Down Expand Up @@ -202,7 +205,7 @@ cardanoRestoreBench tr c socketFile = do
np
vData
"seq.timelog"
(walletSeq "Seq Empty Wallet" networkProxy mkSeqStateFromRootXPrv)
(walletSeq "Seq Empty Wallet" $ mkSeqState networkProxy)

, bench_restoration @_ @ByronKey
networkProxy
Expand All @@ -229,7 +232,7 @@ cardanoRestoreBench tr c socketFile = do
np
vData
"1-percent-seq.timelog"
(walletSeq "Seq 1% Wallet" networkProxy $ mkSeqAnyState @1)
(walletSeq "Seq 1% Wallet" $ mkSeqAnyState' @1 networkProxy)
]
where
walletRnd
Expand Down Expand Up @@ -264,6 +267,22 @@ cardanoRestoreBench tr c socketFile = do
in
(wid, WalletName wname, s)

mkSeqState
:: forall (n :: NetworkDiscriminant). ()
=> Proxy n
-> (ShelleyKey 'RootK XPrv, Passphrase "encryption")
-> AddressPoolGap
-> SeqState n ShelleyKey
mkSeqState _ = mkSeqStateFromRootXPrv @n

mkSeqAnyState'
:: forall (p :: Nat) (n :: NetworkDiscriminant). ()
=> Proxy n
-> (ShelleyKey 'RootK XPrv, Passphrase "encryption")
-> AddressPoolGap
-> SeqAnyState n ShelleyKey p
mkSeqAnyState' _ = mkSeqAnyState @p @n

networkDescription :: forall n. (NetworkDiscriminantVal n) => Proxy n -> Text
networkDescription _ = networkDiscriminantVal @n

Expand Down

0 comments on commit ce3f2a4

Please sign in to comment.