haskoin-store-0.5.0: src/Network/Haskoin/Store/Data/ImportDB.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Network.Haskoin.Store.Data.ImportDB where
import Conduit
import Control.Applicative
import Control.Monad.Except
import Control.Monad.Trans.Maybe
import qualified Data.ByteString.Short as B.Short
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as M
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as I
import Data.List
import Data.Maybe
import Database.RocksDB as R
import Database.RocksDB.Query as R
import Haskoin
import Network.Haskoin.Store.Data
import Network.Haskoin.Store.Data.HashMap
import Network.Haskoin.Store.Data.KeyValue
import Network.Haskoin.Store.Data.RocksDB
import UnliftIO
data ImportDB = ImportDB
{ importRocksDB :: !(DB, ReadOptions)
, importHashMap :: !(TVar HashMapDB)
, importUnspentMap :: !(TVar UnspentMap)
}
runImportDB ::
(MonadError e m, MonadIO m)
=> DB
-> TVar UnspentMap
-> (ImportDB -> m a)
-> m a
runImportDB db um f = do
hm <- newTVarIO emptyHashMapDB
x <- f ImportDB {importRocksDB = d, importHashMap = hm, importUnspentMap = um}
ops <- hashMapOps <$> readTVarIO hm
writeBatch db ops
return x
where
d = (db, defaultReadOptions)
hashMapOps :: HashMapDB -> [BatchOp]
hashMapOps db =
bestBlockOp (hBest db) <>
blockHashOps (hBlock db) <>
blockHeightOps (hHeight db) <>
txOps (hTx db) <>
outOps (hOut db) <>
balOps (hBalance db) <>
addrTxOps (hAddrTx db) <>
addrOutOps (hAddrOut db) <>
mempoolOps (hMempool db) <>
unspentOps (hUnspent db)
bestBlockOp :: Maybe BlockHash -> [BatchOp]
bestBlockOp Nothing = []
bestBlockOp (Just b) = [insertOp BestKey b]
blockHashOps :: HashMap BlockHash BlockData -> [BatchOp]
blockHashOps = map f . M.toList
where
f (h, d) = insertOp (BlockKey h) d
blockHeightOps :: HashMap BlockHeight [BlockHash] -> [BatchOp]
blockHeightOps = map f . M.toList
where
f (g, ls) = insertOp (HeightKey g) ls
txOps :: HashMap TxHash Transaction -> [BatchOp]
txOps = map f . M.toList
where
f (h, t) = insertOp (TxKey h) t
outOps :: HashMap TxHash (IntMap Output) -> [BatchOp]
outOps = concatMap (uncurry f) . M.toList
where
f h = map (uncurry (g h)) . I.toList
g h i = insertOp (OutputKey (OutPoint h (fromIntegral i)))
balOps :: HashMap Address (Maybe BalVal) -> [BatchOp]
balOps = map (uncurry f) . M.toList
where
f a (Just b) = insertOp (BalKey a) b
f a Nothing = deleteOp (BalKey a)
addrTxOps ::
HashMap Address (HashMap BlockRef (HashMap TxHash Bool)) -> [BatchOp]
addrTxOps = concat . concatMap (uncurry f) . M.toList
where
f a = map (uncurry (g a)) . M.toList
g a b = map (uncurry (h a b)) . M.toList
h a b t True =
insertOp
(AddrTxKey
{ addrTxKey =
AddressTx
{ addressTxAddress = a
, addressTxBlock = b
, addressTxHash = t
}
})
()
h a b t False =
deleteOp
AddrTxKey
{ addrTxKey =
AddressTx
{ addressTxAddress = a
, addressTxBlock = b
, addressTxHash = t
}
}
addrOutOps ::
HashMap Address (HashMap BlockRef (HashMap OutPoint (Maybe OutVal)))
-> [BatchOp]
addrOutOps = concat . concatMap (uncurry f) . M.toList
where
f a = map (uncurry (g a)) . M.toList
g a b = map (uncurry (h a b)) . M.toList
h a b p (Just l) =
insertOp
(AddrOutKey {addrOutKeyA = a, addrOutKeyB = b, addrOutKeyP = p})
l
h a b p Nothing =
deleteOp AddrOutKey {addrOutKeyA = a, addrOutKeyB = b, addrOutKeyP = p}
mempoolOps ::
HashMap PreciseUnixTime (HashMap TxHash Bool) -> [BatchOp]
mempoolOps = concatMap (uncurry f) . M.toList
where
f u = map (uncurry (g u)) . M.toList
g u t True = insertOp (MemKey u t) ()
g u t False = deleteOp (MemKey u t)
unspentOps :: HashMap TxHash (IntMap (Maybe Unspent)) -> [BatchOp]
unspentOps = concatMap (uncurry f) . M.toList
where
f h = map (uncurry (g h)) . I.toList
g h i (Just u) =
insertOp
(UnspentKey (OutPoint h (fromIntegral i)))
UnspentVal
{ unspentValAmount = unspentAmount u
, unspentValBlock = unspentBlock u
, unspentValScript = B.Short.fromShort (unspentScript u)
}
g h i Nothing = deleteOp (UnspentKey (OutPoint h (fromIntegral i)))
isInitializedI :: MonadIO m => ImportDB -> m (Either InitException Bool)
isInitializedI ImportDB {importRocksDB = db}= isInitialized db
getBestBlockI :: MonadIO m => ImportDB -> m (Maybe BlockHash)
getBestBlockI ImportDB {importHashMap = hm, importRocksDB = db} =
runMaybeT $ MaybeT (getBestBlock hm) <|> MaybeT (getBestBlock db)
getBlocksAtHeightI :: MonadIO m => ImportDB -> BlockHeight -> m [BlockHash]
getBlocksAtHeightI ImportDB {importHashMap = hm, importRocksDB = db} bh = do
xs <- getBlocksAtHeight hm bh
ys <- getBlocksAtHeight db bh
return . nub $ xs <> ys
getBlockI :: MonadIO m => ImportDB -> BlockHash -> m (Maybe BlockData)
getBlockI ImportDB {importRocksDB = db, importHashMap = hm} bh =
runMaybeT $ MaybeT (getBlock hm bh) <|> MaybeT (getBlock db bh)
getTransactionI ::
MonadIO m => ImportDB -> TxHash -> m (Maybe Transaction)
getTransactionI ImportDB {importRocksDB = db, importHashMap = hm} th =
runMaybeT $ do
tx <- MaybeT (getTransaction hm th) <|> MaybeT (getTransaction db th)
outs <-
forM (take (length (transactionOutputs tx)) [0 ..]) $ \i ->
fromMaybe (transactionOutputs tx !! fromIntegral i) <$>
getOutput hm (OutPoint th i)
return tx {transactionOutputs = outs}
getOutputI :: MonadIO m => ImportDB -> OutPoint -> m (Maybe Output)
getOutputI ImportDB {importRocksDB = db, importHashMap = hm} op =
runMaybeT $ MaybeT (getOutput hm op) <|> MaybeT (getOutput db op)
getBalanceI :: MonadIO m => ImportDB -> Address -> m Balance
getBalanceI ImportDB {importRocksDB = db, importHashMap = hm} a =
getBalanceH <$> readTVarIO hm <*> pure a >>= \case
Just b -> return b
Nothing -> getBalance db a
getUnspentI :: MonadIO m => ImportDB -> OutPoint -> m (Maybe Unspent)
getUnspentI ImportDB { importRocksDB = (db, opts)
, importHashMap = hm
, importUnspentMap = um
} p =
runMaybeT $
MaybeT (getUnspent hm p) <|>
MaybeT (getUnspent um p) <|>
MaybeT (getUnspentDB db opts p)
instance MonadIO m => StoreRead ImportDB m where
isInitialized = isInitializedI
getBestBlock = getBestBlockI
getBlocksAtHeight = getBlocksAtHeightI
getBlock = getBlockI
getTransaction = getTransactionI
getOutput = getOutputI
getBalance = getBalanceI
instance MonadIO m => StoreWrite ImportDB m where
setInit ImportDB {importHashMap = hm, importRocksDB = (db, _)} =
setInit hm >> setInitDB db
setBest ImportDB {importHashMap = hm} = setBest hm
insertBlock ImportDB {importHashMap = hm} = insertBlock hm
insertAtHeight ImportDB {importHashMap = hm} = insertAtHeight hm
insertTx ImportDB {importHashMap = hm} = insertTx hm
insertOutput ImportDB {importHashMap = hm} = insertOutput hm
setBalance ImportDB {importHashMap = hm} = setBalance hm
insertAddrTx ImportDB {importHashMap = hm} = insertAddrTx hm
removeAddrTx ImportDB {importHashMap = hm} = removeAddrTx hm
insertAddrUnspent ImportDB {importHashMap = hm} = insertAddrUnspent hm
removeAddrUnspent ImportDB {importHashMap = hm} = removeAddrUnspent hm
insertMempoolTx ImportDB {importHashMap = hm} = insertMempoolTx hm
deleteMempoolTx ImportDB {importHashMap = hm} = deleteMempoolTx hm
instance MonadIO m => UnspentStore ImportDB m where
addUnspent ImportDB {importHashMap = hm, importUnspentMap = um} u =
addUnspent hm u >> addUnspent um u
delUnspent ImportDB {importHashMap = hm} = delUnspent hm
getUnspent = getUnspentI