Skip to content

Commit

Permalink
Adjust MVar DBLayer to use DB.Model
Browse files Browse the repository at this point in the history
  • Loading branch information
rvl committed Aug 27, 2019
1 parent 21204d0 commit cac0849
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 142 deletions.
2 changes: 1 addition & 1 deletion lib/core/src/Cardano/Wallet/DB.hs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ newtype ErrWalletAlreadyExists
-- wallet. Some other may not because they are information valid for all wallets
-- (like for instance, the last known network tip).
newtype PrimaryKey key = PrimaryKey key
deriving (Eq, Ord)
deriving (Show, Eq, Ord)

-- | Clean a database by removing all wallets.
cleanDB :: Monad m => DBLayer m s t k -> m ()
Expand Down
154 changes: 49 additions & 105 deletions lib/core/src/Cardano/Wallet/DB/MVar.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}

Expand All @@ -23,140 +22,77 @@ import Cardano.Wallet.DB
, ErrWalletAlreadyExists (..)
, PrimaryKey (..)
)
import Cardano.Wallet.DB.Model
import Cardano.Wallet.Primitive.AddressDerivation
( Depth (..), XPrv )
import Cardano.Wallet.Primitive.Model
( Wallet )
import Cardano.Wallet.Primitive.Types
( Hash
, SortOrder (..)
, Tx
, TxMeta (slotId)
, WalletId
, WalletMetadata
, isWithinRange
)
( Hash, WalletId )
import Control.Concurrent.MVar
( MVar, modifyMVar, newMVar, readMVar, withMVar )
( MVar, modifyMVar, newMVar, withMVar )
import Control.DeepSeq
( NFData, deepseq )
import Control.Monad
( (>=>) )
import Control.Monad.Trans.Except
( ExceptT (..), runExceptT )
import Data.List
( sortBy )
import Data.Map.Strict
( Map )
import Data.Ord
( Down (..), comparing )

import qualified Data.Map.Strict as Map

data Database s t k = Database
{ wallet :: !(Wallet s t)
, metadata :: !WalletMetadata
, txHistory :: !(Map (Hash "Tx") (Tx t, TxMeta))
, xprv :: !(Maybe (k 'RootK XPrv, Hash "encryption"))
}
import Data.Bifunctor
( first )
import Data.Tuple
( swap )

-- | Instantiate a new in-memory "database" layer that simply stores data in
-- a local MVar. Data vanishes if the software is shut down.
newDBLayer :: forall s t k. (NFData (k 'RootK XPrv)) => IO (DBLayer IO s t k)
newDBLayer = do
lock <- newMVar ()
db <- newMVar (mempty :: Map (PrimaryKey WalletId) (Database s t k))
db <- newMVar (emptyDatabase :: Database (PrimaryKey WalletId) s t (k 'RootK XPrv, Hash "encryption"))
return $ DBLayer

{-----------------------------------------------------------------------
Wallets
-----------------------------------------------------------------------}

{ createWallet = \key@(PrimaryKey wid) cp meta -> ExceptT $ do
let alter = \case
Nothing ->
Right $ Just $ Database cp meta mempty Nothing
Just _ ->
Left (ErrWalletAlreadyExists wid)
cp `deepseq` meta `deepseq` alterMVar db alter key

, removeWallet = \key@(PrimaryKey wid) -> ExceptT $ do
let alter = \case
Nothing ->
Left (ErrNoSuchWallet wid)
Just _ ->
Right Nothing
alterMVar db alter key

, listWallets =
Map.keys <$> readMVar db
{ createWallet = \pk cp meta -> ExceptT $ do
cp `deepseq` meta `deepseq` alterDB toErrWalletAlreadyExists db (mCreateWallet pk cp meta)

, removeWallet = ExceptT . alterDB toErrNoSuchWallet db . mRemoveWallet

, listWallets = readDB db mListWallets

{-----------------------------------------------------------------------
Checkpoints
-----------------------------------------------------------------------}

, putCheckpoint = \key@(PrimaryKey wid) cp -> ExceptT $ do
let alter = \case
Nothing ->
Left (ErrNoSuchWallet wid)
Just (Database _ meta history k) ->
Right $ Just $ Database cp meta history k
cp `deepseq` alterMVar db alter key
, putCheckpoint = \pk cp -> ExceptT $ do
cp `deepseq` alterDB toErrNoSuchWallet db (mPutCheckpoint pk cp)

, readCheckpoint = \key ->
fmap wallet . Map.lookup key <$> readMVar db
, readCheckpoint = readDB db . mReadCheckpoint

{-----------------------------------------------------------------------
Wallet Metadata
-----------------------------------------------------------------------}

, putWalletMeta = \key@(PrimaryKey wid) meta -> ExceptT $ do
let alter = \case
Nothing ->
Left (ErrNoSuchWallet wid)
Just (Database cp _ history k) ->
Right $ Just $ Database cp meta history k
meta `deepseq` alterMVar db alter key
, putWalletMeta = \pk meta -> ExceptT $ do
meta `deepseq` alterDB toErrNoSuchWallet db (mPutWalletMeta pk meta)

, readWalletMeta = \key -> do
fmap metadata . Map.lookup key <$> readMVar db
, readWalletMeta = readDB db . mReadWalletMeta

{-----------------------------------------------------------------------
Tx History
-----------------------------------------------------------------------}

, putTxHistory = \key@(PrimaryKey wid) txs' -> ExceptT $ do
let alter = \case
Nothing ->
Left (ErrNoSuchWallet wid)
Just (Database cp meta txs k) ->
Right $ Just $ Database cp meta (txs' <> txs) k
txs' `deepseq` alterMVar db alter key

, readTxHistory = \key order range -> let
order' = case order of
Ascending -> comparing slot
Descending -> comparing $ Down . slot
result =
filter ((`isWithinRange` range) . slot)
. sortBy order' . Map.toList . txHistory
slot = slotId . snd . snd
in maybe mempty result . Map.lookup key <$> readMVar db
, putTxHistory = \pk txs -> ExceptT $ do
txs `deepseq` alterDB toErrNoSuchWallet db (mPutTxHistory pk txs)

, readTxHistory = \pk order range ->
readDB db (mReadTxHistory pk order range)

{-----------------------------------------------------------------------
Keystore
-----------------------------------------------------------------------}

, putPrivateKey = \key@(PrimaryKey wid) k -> ExceptT $ do
let alter = \case
Nothing ->
Left (ErrNoSuchWallet wid)
Just (Database cp meta txs _) ->
Right $ Just $ Database cp meta txs (Just k)
k `deepseq` alterMVar db alter key
, putPrivateKey = \pk xprv -> ExceptT $ do
xprv `deepseq` alterDB toErrNoSuchWallet db (mPutPrivateKey pk xprv)

, readPrivateKey = \key ->
(Map.lookup key >=> xprv) <$> readMVar db
, readPrivateKey = readDB db . mReadPrivateKey

{-----------------------------------------------------------------------
Lock
Expand All @@ -172,17 +108,25 @@ newDBLayer = do
-- function yields something. See also:
--
-- [Data.Map.Strict#alterF](https://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Strict.html#v:alterF)
alterMVar
:: Ord k
=> MVar (Map k v) -- MVar holding our mock database
-> (Maybe v -> Either err (Maybe v)) -- An alteration function
-> k -- Key to alter
-> IO (Either err ())
alterMVar db alter key =
modifyMVar db (\m -> bubble m $ Map.alterF alter key m)
alterDB
:: Ord wid
=> (Err wid -> err)
-> MVar (Database wid s t xprv)
-> ModelOp wid s t xprv a
-> IO (Either err a)
alterDB err db op = modifyMVar db (pure . fmap (first err) . swap . op)

readDB
:: (Show wid, Ord wid)
=> MVar (Database wid s t xprv)
-> ModelOp wid s t xprv a
-> IO a
readDB db op = alterDB show db op >>= either throwit pure
where
-- | Re-wrap an error into an MVar result so that it will bubble up
bubble :: Monad m => a -> Either err a -> m (a, Either err ())
bubble a = \case
Left err -> return (a, Left err)
Right a' -> return (a', Right ())
throwit e = error $ "unexpected: " ++ e

toErrNoSuchWallet :: Err (PrimaryKey WalletId) -> ErrNoSuchWallet
toErrNoSuchWallet (NoSuchWallet (PrimaryKey wid)) = ErrNoSuchWallet wid

toErrWalletAlreadyExists :: Err (PrimaryKey WalletId) -> ErrWalletAlreadyExists
toErrWalletAlreadyExists (WalletAlreadyExists (PrimaryKey wid)) = ErrWalletAlreadyExists wid
Loading

0 comments on commit cac0849

Please sign in to comment.