haskoin-wallet-0.0.1: Network/Haskoin/Wallet/Store/Util.hs
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Network.Haskoin.Wallet.Store.Util
( DbWalletGeneric(..)
, DbAccountGeneric(..)
, DbAddressGeneric(..)
, DbCoinGeneric(..)
, DbTxGeneric(..)
, DbTxBlobGeneric(..)
, DbWalletId
, DbAccountId
, DbAddressId
, DbCoinId
, DbTxId
, DbTxBlobId
, Unique(..)
, EntityField(..)
, AccountName
, CoinStatus(..)
, catStatus
, dbGetWallet
, dbGetTxBlob
, liftEither
, liftMaybe
, migrateAll
) where
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Either (EitherT, hoistEither)
import Data.Time (UTCTime)
import Data.Yaml
( ToJSON, toJSON
, object, (.=)
)
import qualified Data.Text as T (pack)
import qualified Data.ByteString as BS (ByteString)
import qualified Data.Conduit as C (transPipe)
import Database.Persist
( PersistStore
, PersistUnique
, PersistQuery
, PersistMonadBackend
, Entity
, EntityField
, Unique
, get
, getBy
, insert_
, insert
, count
, selectKeys
, selectFirst
, selectSource
, updateWhere
, deleteBy
, insertUnique
, updateGet
, replace
, repsert
, insertKey
, insertMany
, delete
, deleteWhere
, update
)
import Database.Persist.TH
( mkPersist
, sqlSettings
, mkMigrate
, persistLowerCase
, share
)
import Network.Haskoin.Wallet.Store.CoinStatus
import Network.Haskoin.Script
import Network.Haskoin.Protocol
import Network.Haskoin.Crypto
import Network.Haskoin.Util
type AccountName = String
liftEither :: Monad m => Either String a -> EitherT String m a
liftEither = hoistEither
liftMaybe :: Monad m => String -> Maybe a -> EitherT String m a
liftMaybe err = liftEither . (maybeToEither err)
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
DbWallet json
name String
type String
master String
accIndex Int
created UTCTime default=CURRENT_TIME
UniqueWalletName name
deriving Show
DbAccount json
name String
index Int
tree String
key String
extIndex Int
extGap Int
intIndex Int
intGap Int
msRequired Int Maybe
msTotal Int Maybe
msKeys [String]
wallet DbWalletId
created UTCTime default=CURRENT_TIME
UniqueAccName name
deriving Show
DbAddress json
base58 String
label String
index Int
tree String
account DbAccountId
internal Bool
created UTCTime default=CURRENT_TIME
UniqueAddress base58
UniqueAddressKey account index internal
deriving Show
DbCoin json
txid String
pos Int
value Int
script String
rdmScript String Maybe
address String
status CoinStatus
account DbAccountId
orphan Bool
created UTCTime default=CURRENT_TIME
CoinOutPoint txid pos
deriving Show
DbTx json
txid String
recipients [String]
value Int
account DbAccountId
orphan Bool
partial Bool
created UTCTime default=CURRENT_TIME
UniqueTx txid account
deriving Show
DbTxBlob json
txid String
value BS.ByteString
created UTCTime default=CURRENT_TIME
UniqueTxBlob txid
deriving Show
|]
dbGetWallet :: (PersistUnique m, PersistMonadBackend m ~ b)
=> String -> EitherT String m (Entity (DbWalletGeneric b))
dbGetWallet name = liftMaybe walletErr =<< (getBy $ UniqueWalletName name)
where
walletErr = unwords ["dbGetWallet: Invalid wallet", name]
dbGetTxBlob :: (PersistUnique m, PersistMonadBackend m ~ b)
=> String -> EitherT String m (Entity (DbTxBlobGeneric b))
dbGetTxBlob tid = liftMaybe txErr =<< (getBy $ UniqueTxBlob tid)
where
txErr = unwords ["dbGetTxBlob: Invalid txid", tid]
instance PersistStore m => PersistStore (EitherT e m) where
type PersistMonadBackend (EitherT e m) = PersistMonadBackend m
get = lift . get
insert = lift . insert
insert_ = lift . insert_
insertMany = lift . insertMany
insertKey k = lift . (insertKey k)
repsert k = lift . (repsert k)
replace k = lift . (replace k)
delete = lift . delete
instance PersistUnique m => PersistUnique (EitherT e m) where
getBy = lift . getBy
deleteBy = lift . deleteBy
insertUnique = lift . insertUnique
instance PersistQuery m => PersistQuery (EitherT e m) where
update k = lift . (update k)
updateGet k = lift . (updateGet k)
updateWhere f = lift . (updateWhere f)
deleteWhere = lift . deleteWhere
selectSource f = (C.transPipe lift) . (selectSource f)
selectFirst f = lift . (selectFirst f)
selectKeys f = (C.transPipe lift) . (selectKeys f)
count = lift . count
{- YAML templates -}
instance ToJSON OutPoint where
toJSON (OutPoint h i) = object
[ (T.pack "TxID") .= encodeTxid h
, (T.pack "Index") .= toJSON i
]
instance ToJSON TxOut where
toJSON (TxOut v s) = object $
[ (T.pack "Value") .= toJSON v
, (T.pack "Raw Script") .= (bsToHex $ encodeScriptOps s)
, (T.pack "Script") .= toJSON s
] ++ scptPair
where scptPair =
either (const [])
(\out -> [(T.pack "Decoded Script") .= toJSON out])
(decodeOutput s)
instance ToJSON TxIn where
toJSON (TxIn o s i) = object $ concat
[ [ (T.pack "OutPoint") .= toJSON o
, (T.pack "Sequence") .= toJSON i
, (T.pack "Raw Script") .= (bsToHex $ encodeScriptOps s)
, (T.pack "Script") .= toJSON s
]
, decoded
]
where decoded = either (const $ either (const []) f $ decodeInput s)
f $ decodeScriptHash s
f inp = [(T.pack "Decoded Script") .= toJSON inp]
instance ToJSON Tx where
toJSON tx@(Tx v is os i) = object
[ (T.pack "TxID") .= encodeTxid (txid tx)
, (T.pack "Version") .= toJSON v
, (T.pack "Inputs") .= (toJSON $ map input $ zip is [0..])
, (T.pack "Outputs") .= (toJSON $ map output $ zip os [0..])
, (T.pack "LockTime") .= toJSON i
]
where input (x,j) = object
[(T.pack $ unwords ["Input", show (j :: Int)]) .= toJSON x]
output (x,j) = object
[(T.pack $ unwords ["Output", show (j :: Int)]) .= toJSON x]
instance ToJSON Script where
toJSON (Script ops) = toJSON $ map show ops
instance ToJSON ScriptOutput where
toJSON (PayPK p) = object
[ (T.pack "PayToPublicKey") .= object
[ (T.pack "Public Key") .= (bsToHex $ encode' p)
]
]
toJSON (PayPKHash a) = object
[ (T.pack "PayToPublicKeyHash") .= object
[ (T.pack "Address Hash160") .= (bsToHex $ encode' $ getAddress a)
, (T.pack "Address Base58") .= addrToBase58 a
]
]
toJSON (PayMulSig ks r) = object
[ (T.pack "PayToMultiSig") .= object
[ (T.pack "Required Keys (M)") .= toJSON r
, (T.pack "Public Keys") .= (toJSON $ map (bsToHex . encode') ks)
]
]
toJSON (PayScriptHash a) = object
[ (T.pack "PayToScriptHash") .= object
[ (T.pack "Address Hash160") .= (bsToHex $ encode' $ getAddress a)
, (T.pack "Address Base58") .= addrToBase58 a
]
]
instance ToJSON ScriptInput where
toJSON (SpendPK s) = object
[ (T.pack "SpendPublicKey") .= object
[ (T.pack "Signature") .= toJSON s
]
]
toJSON (SpendPKHash s p) = object
[ (T.pack "SpendPublicKeyHash") .= object
[ (T.pack "Signature") .= toJSON s
, (T.pack "Public Key") .= (bsToHex $ encode' p)
, (T.pack "Sender Addr") .= addrToBase58 (pubKeyAddr p)
]
]
toJSON (SpendMulSig sigs r) = object
[ (T.pack "SpendMultiSig") .= object
[ (T.pack "Required Keys (M)") .= toJSON r
, (T.pack "Signatures") .= (toJSON $ map toJSON sigs)
]
]
instance ToJSON ScriptHashInput where
toJSON (ScriptHashInput s r) = object
[ (T.pack "SpendScriptHash") .= object
[ (T.pack "ScriptInput") .= toJSON s
, (T.pack "RedeemScript") .= toJSON r
, (T.pack "Raw Redeem Script") .=
(bsToHex $ encodeScriptOps $ encodeOutput r)
, (T.pack "Sender Addr") .= (addrToBase58 $ scriptAddr r)
]
]
instance ToJSON TxSignature where
toJSON ts@(TxSignature _ h) = object
[ (T.pack "Raw Sig") .= (bsToHex $ encodeSig ts)
, (T.pack "SigHash") .= toJSON h
]
instance ToJSON SigHash where
toJSON sh = case sh of
(SigAll acp) -> object
[ (T.pack "Type") .= T.pack "SigAll"
, (T.pack "AnyoneCanPay") .= acp
]
(SigNone acp) -> object
[ (T.pack "Type") .= T.pack "SigNone"
, (T.pack "AnyoneCanPay") .= acp
]
(SigSingle acp) -> object
[ (T.pack "Type") .= T.pack "SigSingle"
, (T.pack "AnyoneCanPay") .= acp
]
(SigUnknown acp v) -> object
[ (T.pack "Type") .= T.pack "SigUnknown"
, (T.pack "AnyoneCanPay") .= acp
, (T.pack "Value") .= v
]