module Network.Bitcoin.Wallet ( Client
, getClient
, BitcoindInfo(..)
, getBitcoindInfo
, getNewAddress
, getAccountAddress
, getAccount
, setAccount
, getAddressesByAccount
, sendToAddress
, AddressInfo(..)
, listAddressGroupings
, Signature
, signMessage
, verifyMessage
, getReceivedByAddress
, getReceivedByAddress'
, getReceivedByAccount
, getReceivedByAccount'
, getBalance
, getBalance'
, getBalance''
, moveBitcoins
, sendFromAccount
, sendMany
, ReceivedByAddress(..)
, listReceivedByAddress
, listReceivedByAddress'
, ReceivedByAccount(..)
, listReceivedByAccount
, listReceivedByAccount'
, listTransactions
, listTransactions'
, listAccounts
, SinceBlock(..)
, SimpleTransaction(..)
, TransactionCategory(..)
, listSinceBlock
, listSinceBlock'
, DetailedTransaction(..)
, DetailedTransactionDetails(..)
, getTransaction
, backupWallet
, keyPoolRefill
, unlockWallet
, lockWallet
, changePassword
, encryptWallet
, isAddressValid
) where
import Control.Applicative
import Control.Monad
import Data.Aeson as A
import qualified Data.HashMap.Lazy as HM
import Data.Maybe
import Data.Text
import Data.Time.Clock.POSIX
import Data.Vector as V hiding ((++))
import Network.Bitcoin.BlockChain (BlockHash)
import Network.Bitcoin.Internal
import Network.Bitcoin.RawTransaction (RawTransaction)
data BitcoindInfo =
BitcoindInfo {
bitcoinVersion :: Integer
, protocolVersion :: Integer
, walletVersion :: Integer
, balance :: BTC
, numBlocks :: Integer
, numConnections :: Integer
, proxy :: Text
, generationDifficulty :: Double
, onTestNetwork :: Bool
, keyPoolOldest :: Integer
, keyPoolSize :: Integer
, transactionFeePaid :: BTC
, unlockedUntil :: Maybe Integer
, bitcoindErrors :: Text
}
deriving ( Show, Read, Ord, Eq )
instance FromJSON BitcoindInfo where
parseJSON (Object o) = BitcoindInfo <$> o .: "version"
<*> o .: "protocolversion"
<*> o .: "walletversion"
<*> o .: "balance"
<*> o .: "blocks"
<*> o .: "connections"
<*> o .: "proxy"
<*> o .: "difficulty"
<*> o .: "testnet"
<*> o .: "keypoololdest"
<*> o .: "keypoolsize"
<*> o .: "paytxfee"
<*> o .:? "unlocked_until"
<*> o .: "errors"
parseJSON _ = mzero
getBitcoindInfo :: Client -> IO BitcoindInfo
getBitcoindInfo client = callApi client "getinfo" []
getNewAddress :: Client -> Maybe Account -> IO Address
getNewAddress client ma = let acc = fromMaybe "" ma
in callApi client "getnewaddress" [ tj acc ]
getAccountAddress :: Client -> Account -> IO Address
getAccountAddress client acc = callApi client "getaccountaddress" [ tj acc ]
setAccount :: Client -> Address -> Account -> IO ()
setAccount client addr acc = unNil <$> callApi client "setaccount" [ tj addr, tj acc ]
getAccount :: Client -> Address -> IO Account
getAccount client addr = callApi client "getaccount" [ tj addr ]
getAddressesByAccount :: Client -> Account -> IO (Vector Address)
getAddressesByAccount client acc = callApi client "getaddressesbyaccount" [ tj acc ]
sendToAddress :: Client
-> Address
-> BTC
-> Maybe Text
-> Maybe Text
-> IO TransactionID
sendToAddress client addr amount comm comm2 =
callApi client "sendtoaddress" [ tj addr, tj amount, tj comm, tj comm2 ]
data AddressInfo = AddressInfo {
aiAddress :: Address
, aiAmount :: BTC
, aiAccount :: Maybe Account
}
deriving ( Show, Read, Eq, Ord )
instance FromJSON AddressInfo where
parseJSON (A.Array a) | V.length a == 2 = AddressInfo <$> parseJSON (a ! 0)
<*> parseJSON (a ! 1)
<*> pure Nothing
| V.length a == 3 = AddressInfo <$> parseJSON (a ! 0)
<*> parseJSON (a ! 1)
<*> (Just <$> parseJSON (a ! 2))
| otherwise = mzero
parseJSON _ = mzero
listAddressGroupings :: Client
-> IO (Vector (Vector AddressInfo))
listAddressGroupings client =
callApi client "listaddressgroupings" []
type Signature = HexString
signMessage :: Client
-> Address
-> Text
-> IO Signature
signMessage client addr msg = callApi client "signmessage" [ tj addr, tj msg ]
verifyMessage :: Client
-> Address
-> Signature
-> Text
-> IO Bool
verifyMessage client addr sig msg =
callApi client "verifymessage" [ tj addr, tj sig, tj msg ]
getReceivedByAddress :: Client -> Address -> IO BTC
getReceivedByAddress client addr =
callApi client "getreceivedbyaddress" [ tj addr ]
getReceivedByAddress' :: Client
-> Address
-> Int
-> IO BTC
getReceivedByAddress' client addr minconf =
callApi client "getreceivedbyaddress" [ tj addr, tj minconf ]
getReceivedByAccount :: Client -> Account -> IO BTC
getReceivedByAccount client acc =
callApi client "getreceivedbyaccount" [ tj acc ]
getReceivedByAccount' :: Client
-> Account
-> Int
-> IO BTC
getReceivedByAccount' client acc minconf =
callApi client "getreceivedbyaccount" [ tj acc, tj minconf ]
getBalance :: Client
-> IO BTC
getBalance client =
callApi client "getbalance" []
getBalance' :: Client
-> Account
-> IO BTC
getBalance' client acc =
callApi client "getbalance" [ tj acc ]
getBalance'' :: Client
-> Account
-> Int
-> IO BTC
getBalance'' client acc minconf =
callApi client "getbalance" [ tj acc, tj minconf ]
moveBitcoins :: Client
-> Account
-> Account
-> BTC
-> Text
-> IO ()
moveBitcoins client from to amt comm =
stupidAPI <$> callApi client "move" [ tj from, tj to, tj amt, tj one, tj comm ]
where one = 1 :: Int
stupidAPI :: Bool -> ()
stupidAPI = const ()
sendFromAccount :: Client
-> Account
-> Address
-> BTC
-> Maybe Text
-> Maybe Text
-> IO TransactionID
sendFromAccount client from to amount comm comm2 =
callApi client "sendfrom" [ tj from, tj to, tj amount, tj one, tj comm, tj comm2 ]
where one = 1 :: Int
sendMany :: Client
-> Account
-> Vector (Address, BTC)
-> Maybe Text
-> IO TransactionID
sendMany client acc amounts comm =
callApi client "sendmany" [ tj acc, tj $ AA amounts, tj (1 :: Int), tj comm ]
data ReceivedByAddress =
ReceivedByAddress {
recvAddress :: Address
, recvAccount :: Account
, recvAmount :: BTC
, recvNumConfirmations :: Integer
}
deriving ( Show, Read, Ord, Eq )
instance FromJSON ReceivedByAddress where
parseJSON (Object o) = ReceivedByAddress <$> o .: "address"
<*> o .: "account"
<*> o .: "amount"
<*> o .: "confirmations"
parseJSON _ = mzero
listReceivedByAddress :: Client -> IO (Vector ReceivedByAddress)
listReceivedByAddress client = listReceivedByAddress' client 1 False
listReceivedByAddress' :: Client
-> Int
-> Bool
-> IO (Vector ReceivedByAddress)
listReceivedByAddress' client minconf includeEmpty =
callApi client "listreceivedbyaddress" [ tj minconf, tj includeEmpty ]
data ReceivedByAccount =
ReceivedByAccount { raccAccount :: Account
, raccAmount :: BTC
, raccNumConfirmations :: Integer
}
deriving ( Show, Read, Ord, Eq )
instance FromJSON ReceivedByAccount where
parseJSON (Object o) = ReceivedByAccount <$> o .: "account"
<*> o .: "amount"
<*> o .: "confirmations"
parseJSON _ = mzero
listReceivedByAccount :: Client -> IO (Vector ReceivedByAccount)
listReceivedByAccount client = listReceivedByAccount' client 1 False
listReceivedByAccount' :: Client
-> Int
-> Bool
-> IO (Vector ReceivedByAccount)
listReceivedByAccount' client minconf includeEmpty =
callApi client "listreceivedbyaccount" [ tj minconf, tj includeEmpty ]
data SinceBlock =
SinceBlock { strransactions :: Vector SimpleTransaction
, sbLastBlockHash :: BlockHash
}
deriving ( Show, Ord, Eq )
instance FromJSON SinceBlock where
parseJSON (Object o) = SinceBlock <$> o .: "transactions"
<*> o .: "lastblock"
parseJSON _ = mzero
data SimpleTransaction =
SimpleTransaction {
stReceivingAccount :: Account
, stAddress :: Maybe Address
, stCategory :: TransactionCategory
, stFee :: Maybe BTC
, stAmount :: BTC
, stConfirmations :: Maybe Integer
, stBlockHash :: Maybe BlockHash
, stBlockIndex :: Maybe Integer
, stBlockTime :: Maybe POSIXTime
, stTransactionId :: Maybe TransactionID
, stWalletConflicts :: Maybe (Vector TransactionID)
, stTime :: POSIXTime
, stTimeReceived :: Maybe POSIXTime
, stComment :: Maybe Text
, stTo :: Maybe Text
, stOtherAccount :: Maybe Account
}
deriving ( Show, Ord, Eq )
instance FromJSON SimpleTransaction where
parseJSON (Object o) =
SimpleTransaction
<$> o .: "account"
<*> o .:? "address"
<*> o .: "category"
<*> o .:? "fee"
<*> o .: "amount"
<*> o .:? "confirmations"
<*> o .:? "blockhash"
<*> o .:? "blockindex"
<*> (fmap fromInteger <$> o .:? "blocktime")
<*> o .:? "txid"
<*> o .:? "walletconflicts"
<*> (fromInteger <$> o .: "time")
<*> (fmap fromInteger <$> o .:? "timereceived")
<*> o .:? "comment"
<*> o .:? "to"
<*> o .:? "otheraccount"
parseJSON _ = mzero
data TransactionCategory = TCSend
| TCOrphan
| TCImmature
| TCGenerate
| TCReceive
| TCMove
| TCErrorUnexpected Text
deriving ( Show, Read, Ord, Eq )
instance FromJSON TransactionCategory where
parseJSON (String s) = return $ createTC s
where createTC :: Text -> TransactionCategory
createTC "send" = TCSend
createTC "orphan" = TCOrphan
createTC "immature" = TCImmature
createTC "generate" = TCGenerate
createTC "receive" = TCReceive
createTC "move" = TCMove
createTC uc = TCErrorUnexpected uc
parseJSON _ = mzero
listSinceBlock :: Client
-> BlockHash
-> Maybe Int
-> IO (SinceBlock)
listSinceBlock client blockHash conf =
listSinceBlock' client (Just blockHash) conf
listSinceBlock' :: Client
-> Maybe BlockHash
-> Maybe Int
-> IO (SinceBlock)
listSinceBlock' client mblockHash mminConf =
callApi client "listsinceblock" $ tja mblockHash ++ tja mminConf
listTransactions :: Client
-> Account
-> Int
-> Int
-> IO (Vector SimpleTransaction)
listTransactions client account size from =
listTransactions' client (Just account) (Just size) (Just from)
listTransactions' :: Client
-> Maybe Account
-> Maybe Int
-> Maybe Int
-> IO (Vector SimpleTransaction)
listTransactions' client maccount mcount mfrom =
callApi client "listtransactions" $ [ tjm "*" maccount ] ++ tja mcount ++ tja mfrom
listAccounts :: Client
-> Maybe Int
-> IO (HM.HashMap Account BTC)
listAccounts client mconf =
callApi client "listaccounts" [ tjm 1 mconf ]
data DetailedTransaction =
DetailedTransaction {
dtAmount :: BTC
, dtFee :: Maybe BTC
, dtConfirmations :: Maybe Integer
, dtTransactionId :: Maybe TransactionID
, dtWalletConflicts :: Maybe (Vector TransactionID)
, dtTime :: POSIXTime
, dtTimeReceived :: Maybe POSIXTime
, dtComment :: Maybe Text
, dtTo :: Maybe Text
, dtDetails :: Vector DetailedTransactionDetails
, dtHex :: RawTransaction
}
deriving ( Show, Ord, Eq )
instance FromJSON DetailedTransaction where
parseJSON (Object o) =
DetailedTransaction
<$> o .: "amount"
<*> o .:? "fee"
<*> o .: "confirmations"
<*> o .:? "txid"
<*> o .:? "walletconflicts"
<*> (fromInteger <$> o .: "time")
<*> (fmap fromInteger <$> o .:? "timereceived")
<*> o .:? "comment"
<*> o .:? "to"
<*> o .: "details"
<*> o .: "hex"
parseJSON _ = mzero
data DetailedTransactionDetails =
DetailedTransactionDetails {
dtdReceivingAccount :: Account
, dtdAddress :: Address
, dtdCategory :: TransactionCategory
, dtdAmount :: BTC
}
deriving ( Show, Ord, Eq )
instance FromJSON DetailedTransactionDetails where
parseJSON (Object o) = DetailedTransactionDetails <$> o .: "account"
<*> o .: "address"
<*> o .: "category"
<*> o .: "amount"
parseJSON _ = mzero
getTransaction :: Client
-> TransactionID
-> IO (DetailedTransaction)
getTransaction client txid =
callApi client "gettransaction" [ tj txid ]
backupWallet :: Client
-> FilePath
-> IO ()
backupWallet client fp =
unNil <$> callApi client "backupwallet" [ tj fp ]
keyPoolRefill :: Client -> IO ()
keyPoolRefill client = unNil <$> callApi client "keypoolrefill" []
unlockWallet :: Client
-> Text
-> Integer
-> IO ()
unlockWallet client pass timeout =
unNil <$> callApi client "walletpassphrase" [ tj pass, tj timeout ]
changePassword :: Client
-> Text
-> Text
-> IO ()
changePassword client old new =
unNil <$> callApi client "walletpassphrasechange" [ tj old, tj new ]
lockWallet :: Client -> IO ()
lockWallet client = unNil <$> callApi client "walletlock" []
encryptWallet :: Client -> Text -> IO ()
encryptWallet client pass = stupidAPI <$> callApi client "encryptwallet" [ tj pass ]
where
stupidAPI :: Text -> ()
stupidAPI = const ()
data IsValid = IsValid { getValid :: Bool }
instance FromJSON IsValid where
parseJSON (Object o) = IsValid <$> o .: "isvalid"
parseJSON _ = mzero
isAddressValid :: Client -> Address -> IO Bool
isAddressValid client addr = getValid <$> callApi client "validateaddress" [ tj addr ]