haskoin-wallet-0.0.1: Network/Haskoin/Wallet/Store/DbAccount.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
module Network.Haskoin.Wallet.Store.DbAccount
( dbGetAcc
, dbNewAcc
, dbNewMS
, dbAddKeys
, yamlAcc
, isMSAcc
) where
import Control.Monad (when, unless)
import Control.Monad.Trans (liftIO)
import Control.Monad.Trans.Either (EitherT, left)
import Data.Time (getCurrentTime)
import Data.Yaml (Value, object, (.=))
import Data.Maybe (fromJust, isJust)
import Data.List (nub)
import qualified Data.Text as T (pack)
import Database.Persist
( PersistQuery
, PersistUnique
, PersistStore
, PersistMonadBackend
, Entity(..)
, getBy
, insert_
, update
, count
, replace
, (==.), (=.)
)
import Network.Haskoin.Wallet.Keys
import Network.Haskoin.Wallet.Manager
import Network.Haskoin.Wallet.Store.Util
yamlAcc :: DbAccountGeneric b -> Value
yamlAcc acc = object $ concat
[ [ "Name" .= dbAccountName acc
, "Tree" .= dbAccountTree acc
]
, datType, datWarn
]
where msReq = fromJust $ dbAccountMsRequired acc
msTot = fromJust $ dbAccountMsTotal acc
ms = unwords [show msReq,"of",show msTot]
miss = msTot - length (dbAccountMsKeys acc) - 1
datType | isMSAcc acc = ["Type" .= unwords [ "Multisig", ms ]]
| otherwise = ["Type" .= ("Regular" :: String)]
datWarn | isMSAcc acc && miss > 0 =
[ (T.pack "Warning") .=
unwords [show miss,"multisig keys missing"]
]
| otherwise = []
isMSAcc :: DbAccountGeneric b -> Bool
isMSAcc acc = (isJust $ dbAccountMsRequired acc) &&
(isJust $ dbAccountMsTotal acc)
dbGetAcc :: (PersistUnique m, PersistMonadBackend m ~ b)
=> String
-> EitherT String m (Entity (DbAccountGeneric b))
dbGetAcc name = liftMaybe accErr =<< (getBy $ UniqueAccName name)
where
accErr = unwords ["dbGetAcc: Invalid account", name]
dbNewAcc :: ( PersistUnique m
, PersistQuery m
, PersistMonadBackend m ~ b
)
=> String -> EitherT String m (DbAccountGeneric b)
dbNewAcc name = do
time <- liftIO getCurrentTime
(Entity wk w) <- dbGetWallet "main"
let keyM = loadMasterKey =<< (xPrvImport $ dbWalletMaster w)
master <- liftMaybe keyErr keyM
let deriv = fromIntegral $ dbWalletAccIndex w + 1
(k,i) = head $ accPubKeys master deriv
acc = DbAccount name
(fromIntegral i)
(concat ["m/",show i,"'/"])
(xPubExport $ getAccPubKey k)
(-1) (-1) (-1) (-1)
Nothing Nothing [] wk time
insert_ acc
update wk [DbWalletAccIndex =. fromIntegral i]
return acc
where
keyErr = "dbNewAcc: Could not load master key"
dbNewMS :: ( PersistUnique m
, PersistQuery m
, PersistMonadBackend m ~ b
)
=> String -> Int -> Int -> [XPubKey]
-> EitherT String m (DbAccountGeneric b)
dbNewMS name m n mskeys = do
time <- liftIO getCurrentTime
let keys = nub mskeys
unless (n >= 1 && n <= 16 && m >= 1 && m <= n) $ left
"cmdNewMS: Invalid multisig parameters"
unless (length keys < n) $ left
"cmdNewMS: Too many keys"
(Entity wk w) <- dbGetWallet "main"
let keyM = loadMasterKey =<< (xPrvImport $ dbWalletMaster w)
master <- liftMaybe keyErr keyM
let deriv = fromIntegral $ dbWalletAccIndex w + 1
(k,i) = head $ accPubKeys master deriv
acc = DbAccount name
(fromIntegral i)
(concat ["m/",show i,"'/"])
(xPubExport $ getAccPubKey k)
(-1) (-1) (-1) (-1)
(Just m) (Just n)
(map xPubExport keys)
wk time
insert_ acc
update wk [DbWalletAccIndex =. fromIntegral i]
return acc
where
keyErr = "dbNewMS: Could not load master key"
dbAddKeys :: ( PersistUnique m
, PersistQuery m
, PersistMonadBackend m ~ b
)
=> AccountName -> [XPubKey]
-> EitherT String m (DbAccountGeneric b)
dbAddKeys name keys
| null keys = left "dbAddKeys: Keys can not be empty"
| otherwise = do
(Entity ai acc) <- dbGetAcc name
unless (isMSAcc acc) $ left $
"dbAddKeys: Can only add keys to a multisig account"
exists <- mapM (\x -> count [DbAccountKey ==. (xPubExport x)]) keys
unless (sum exists == 0) $ left $
"dbAddKeys: Can not add your own keys to a multisig account"
prevKeys <- liftMaybe keyErr $ mapM xPubImport $ dbAccountMsKeys acc
when (length prevKeys == (fromJust $ dbAccountMsTotal acc) - 1) $ left $
"dbAddKeys: Account is complete. No more keys can be added"
let newKeys = nub $ prevKeys ++ keys
newAcc = acc{ dbAccountMsKeys = map xPubExport newKeys }
unless (length newKeys < (fromJust $ dbAccountMsTotal acc)) $ left $
"dbAddKeys: Too many keys"
replace ai newAcc
return newAcc
where
keyErr = "dbAddKeys: Invalid keys found in account"