Skip to content
Snippets Groups Projects
Commit 405ae318 authored by Ramakrishnan Muthukrishnan's avatar Ramakrishnan Muthukrishnan
Browse files

refactor code to accomodate SQLite based voucher database

parent 4e01a543
No related branches found
No related tags found
1 merge request!26Initial implementation of Persistence using sqlite
...@@ -25,7 +25,7 @@ import Network.Wai.Middleware.RequestLogger ...@@ -25,7 +25,7 @@ import Network.Wai.Middleware.RequestLogger
) )
import PaymentServer.Persistence import PaymentServer.Persistence
( memory ( memory
, Database(Memory, SQLite3) , getDBConnection
) )
import PaymentServer.Issuer import PaymentServer.Issuer
( trivialIssue ( trivialIssue
...@@ -64,6 +64,11 @@ data Issuer = ...@@ -64,6 +64,11 @@ data Issuer =
| Ristretto | Ristretto
deriving (Show, Eq, Ord, Read) deriving (Show, Eq, Ord, Read)
data Database =
Memory
| SQLite3
deriving (Show, Eq, Ord, Read)
data ServerConfig = ServerConfig data ServerConfig = ServerConfig
{ issuer :: Issuer { issuer :: Issuer
, signingKey :: Maybe Text , signingKey :: Maybe Text
...@@ -111,6 +116,7 @@ main = ...@@ -111,6 +116,7 @@ main =
getDatabase ServerConfig{ database, databasePath } = getDatabase ServerConfig{ database, databasePath } =
case (database, databasePath) of case (database, databasePath) of
(Memory, Nothing) -> Right memory (Memory, Nothing) -> Right memory
(SQLite3, Just path) -> Right (getDBConnection path)
_ -> Left "invalid options" _ -> Left "invalid options"
in do in do
config <- execParser opts config <- execParser opts
......
...@@ -4,9 +4,8 @@ module PaymentServer.Persistence ...@@ -4,9 +4,8 @@ module PaymentServer.Persistence
( Voucher ( Voucher
, Fingerprint , Fingerprint
, RedeemError(NotPaid, AlreadyRedeemed) , RedeemError(NotPaid, AlreadyRedeemed)
, Database(Memory, SQLite3)
, VoucherDatabase(payForVoucher, redeemVoucher) , VoucherDatabase(payForVoucher, redeemVoucher)
, MemoryVoucherDatabase , VoucherDatabaseState(MemoryDB, SQLiteDB)
, memory , memory
, getDBConnection , getDBConnection
-- * for testing -- * for testing
...@@ -50,11 +49,6 @@ data RedeemError = ...@@ -50,11 +49,6 @@ data RedeemError =
| AlreadyRedeemed | AlreadyRedeemed
deriving (Show, Eq) deriving (Show, Eq)
data Database =
Memory
| SQLite3
deriving (Show, Eq, Ord, Read)
-- | A fingerprint cryptographically identifies a redemption of a voucher. -- | A fingerprint cryptographically identifies a redemption of a voucher.
-- When a voucher is redeemed, a number of random tokens are received -- When a voucher is redeemed, a number of random tokens are received
-- alongside it. These tokens are signed to create ZKAPs to return to the -- alongside it. These tokens are signed to create ZKAPs to return to the
...@@ -87,7 +81,7 @@ class VoucherDatabase d where ...@@ -87,7 +81,7 @@ class VoucherDatabase d where
-- in-memory. The state does not outlive the process which creates it (nor -- in-memory. The state does not outlive the process which creates it (nor
-- even the MemoryVoucherDatabase value). This is primarily useful for -- even the MemoryVoucherDatabase value). This is primarily useful for
-- testing. -- testing.
data MemoryVoucherDatabase = data VoucherDatabaseState =
MemoryDB { MemoryDB {
-- | A set of vouchers which have been paid for. -- | A set of vouchers which have been paid for.
paid :: IORef (Set.Set Voucher) paid :: IORef (Set.Set Voucher)
...@@ -95,11 +89,13 @@ data MemoryVoucherDatabase = ...@@ -95,11 +89,13 @@ data MemoryVoucherDatabase =
-- redemption. -- redemption.
, redeemed :: IORef (Map.Map Voucher Fingerprint) , redeemed :: IORef (Map.Map Voucher Fingerprint)
} }
| SQLiteDB { conn :: Sqlite.Connection }
instance VoucherDatabase MemoryVoucherDatabase where instance VoucherDatabase VoucherDatabaseState where
payForVoucher MemoryDB{ paid = paid, redeemed = redeemed } voucher = do payForVoucher MemoryDB{ paid = paid, redeemed = redeemed } voucher = do
modifyIORef paid (Set.insert voucher) modifyIORef paid (Set.insert voucher)
return () return ()
payForVoucher SQLiteDB{ conn = conn } voucher = insertVoucher conn voucher
redeemVoucher MemoryDB{ paid = paid, redeemed = redeemed } voucher fingerprint = do redeemVoucher MemoryDB{ paid = paid, redeemed = redeemed } voucher fingerprint = do
unpaid <- Set.notMember voucher <$> readIORef paid unpaid <- Set.notMember voucher <$> readIORef paid
...@@ -115,26 +111,14 @@ instance VoucherDatabase MemoryVoucherDatabase where ...@@ -115,26 +111,14 @@ instance VoucherDatabase MemoryVoucherDatabase where
return $ Right () return $ Right ()
else else
return $ Left AlreadyRedeemed return $ Left AlreadyRedeemed
redeemVoucher SQLiteDB { conn = conn } voucher fingerprint = do
-- | Create a new, empty MemoryVoucherDatabase. unpaid <- isVoucherUnpaid conn voucher
memory :: IO MemoryVoucherDatabase existingFingerprint <- getVoucherFingerprint conn voucher
memory = do
paid <- newIORef mempty
redeemed <- newIORef mempty
return $ MemoryDB paid redeemed
instance VoucherDatabase Sqlite.Connection where
-- payForVoucher :: Sqlite.Connection -> Voucher -> IO ()
payForVoucher = insertVoucher
-- redeemVoucher :: Sqlite.Connection -> Voucher -> Fingerprint -> IO (Either RedeemError ())
redeemVoucher dbConn voucher fingerprint = do
unpaid <- isVoucherUnpaid dbConn voucher
existingFingerprint <- getVoucherFingerprint dbConn voucher
case (unpaid, existingFingerprint) of case (unpaid, existingFingerprint) of
(True, _) -> (True, _) ->
return $ Left NotPaid return $ Left NotPaid
(False, []) -> do (False, []) -> do
insertVoucherAndFingerprint dbConn voucher fingerprint insertVoucherAndFingerprint conn voucher fingerprint
return $ Right () return $ Right ()
(False, [fingerprint']) -> (False, [fingerprint']) ->
if fingerprint == fingerprint' then if fingerprint == fingerprint' then
...@@ -143,6 +127,13 @@ instance VoucherDatabase Sqlite.Connection where ...@@ -143,6 +127,13 @@ instance VoucherDatabase Sqlite.Connection where
return $ Left AlreadyRedeemed return $ Left AlreadyRedeemed
-- | Create a new, empty MemoryVoucherDatabase.
memory :: IO VoucherDatabaseState
memory = do
paid <- newIORef mempty
redeemed <- newIORef mempty
return $ MemoryDB paid redeemed
instance FromRow Fingerprint where instance FromRow Fingerprint where
fromRow = Sqlite.field fromRow = Sqlite.field
...@@ -164,9 +155,9 @@ insertVoucherAndFingerprint :: Sqlite.Connection -> Voucher -> Fingerprint -> IO ...@@ -164,9 +155,9 @@ insertVoucherAndFingerprint :: Sqlite.Connection -> Voucher -> Fingerprint -> IO
insertVoucherAndFingerprint dbConn voucher fingerprint = insertVoucherAndFingerprint dbConn voucher fingerprint =
Sqlite.execute dbConn "INSERT INTO redeemed (voucher_id, fingerprint) VALUES ((SELECT id FROM vouchers_new WHERE name = ?), ?)" (voucher, fingerprint) Sqlite.execute dbConn "INSERT INTO redeemed (voucher_id, fingerprint) VALUES ((SELECT id FROM vouchers_new WHERE name = ?), ?)" (voucher, fingerprint)
getDBConnection :: Text -> IO Sqlite.Connection getDBConnection :: Text -> IO VoucherDatabaseState
getDBConnection name = do getDBConnection name = do
dbConn <- Sqlite.open (unpack name) dbConn <- Sqlite.open (unpack name)
Sqlite.execute_ dbConn "CREATE TABLE IF NOT EXISTS vouchers (id INTEGER PRIMARY KEY, name TEXT UNIQUE)" Sqlite.execute_ dbConn "CREATE TABLE IF NOT EXISTS vouchers (id INTEGER PRIMARY KEY, name TEXT UNIQUE)"
Sqlite.execute_ dbConn "CREATE TABLE IF NOT EXISTS redeemed (id INTEGER PRIMARY KEY, voucher_id INTEGER, fingerprint TEXT, FOREIGN KEY (voucher_id) REFERENCES vouchers(id))" Sqlite.execute_ dbConn "CREATE TABLE IF NOT EXISTS redeemed (id INTEGER PRIMARY KEY, voucher_id INTEGER, fingerprint TEXT, FOREIGN KEY (voucher_id) REFERENCES vouchers(id))"
return dbConn return $ SQLiteDB dbConn
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment