1
1
{-# LANGUAGE OverloadedStrings #-}
2
2
{-# OPTIONS_GHC -Wall #-}
3
+
3
4
-- | The API exposed in this module should be considered unstable, and is
4
5
-- subject to change between minor revisions.
5
6
--
11
12
-- module is guaranteed to be stable.
12
13
--
13
14
-- If only d changes, then there were no user-facing code changes made.
14
- module Network.Bitcoin.Internal ( module Network.Bitcoin.Types
15
- , Text , Vector
16
- , FromJSON (.. )
17
- , callApi
18
- , getClient
19
- , Nil (.. )
20
- , NilOrArray (.. )
21
- , tj
22
- , tjm
23
- , tja
24
- , AddrAddress (.. )
25
- , BitcoinRpcResponse (.. )
26
- ) where
27
-
28
- import Control.Exception
29
- import Control.Monad
30
- import Control.Applicative
31
- import Data.Aeson
32
- import qualified Data.ByteString as BS
33
- import Data.Maybe
34
- import Data.Text (Text )
35
- import Data.Vector (Vector )
36
- import qualified Data.Vector as V
37
- import Network.Bitcoin.Types
38
- import Network.HTTP.Client
39
- import Network.HTTP.Types.Header
40
-
15
+ module Network.Bitcoin.Internal
16
+ ( module Network.Bitcoin.Types ,
17
+ Text ,
18
+ Vector ,
19
+ FromJSON (.. ),
20
+ callApi ,
21
+ getClient ,
22
+ Nil (.. ),
23
+ NilOrArray (.. ),
24
+ tj ,
25
+ tjm ,
26
+ tja ,
27
+ AddrAddress (.. ),
28
+ BitcoinRpcResponse (.. ),
29
+ )
30
+ where
31
+
32
+ import Control.Applicative
33
+ import Control.Exception
34
+ import Control.Monad
35
+ import Data.Aeson
36
+ import qualified Data.Aeson.Key as AK
37
+ import qualified Data.ByteString as BS
38
+ import Data.Maybe
39
+ import Data.Text (Text )
40
+ import Data.Vector (Vector )
41
+ import qualified Data.Vector as V
42
+ import Network.Bitcoin.Types
43
+ import Network.HTTP.Client
44
+ import Network.HTTP.Types.Header
41
45
42
46
-- | RPC calls return an error object. It can either be empty; or have an
43
47
-- error message + error code.
@@ -56,7 +60,7 @@ data BitcoinRpcResponse a
56
60
| BitcoinRpcResult a
57
61
deriving (Show , Read , Ord , Eq )
58
62
59
- instance FromJSON a => FromJSON (BitcoinRpcResponse a ) where
63
+ instance ( FromJSON a ) => FromJSON (BitcoinRpcResponse a ) where
60
64
parseJSON (Object v) =
61
65
(BitcoinRpcError <$> v .: " error" )
62
66
<|> (BitcoinRpcResult <$> v .: " result" )
@@ -68,19 +72,26 @@ instance FromJSON a => FromJSON (BitcoinRpcResponse a) where
68
72
-- Client encloses a Manager (from http-client) that re-uses
69
73
-- connections for requests, so long as the same Client is
70
74
-- is used for each call.
71
- getClient :: String
72
- -> BS. ByteString
73
- -> BS. ByteString
74
- -> IO Client
75
+ getClient ::
76
+ String ->
77
+ BS. ByteString ->
78
+ BS. ByteString ->
79
+ IO Client
75
80
getClient url user pass = do
76
- url' <- parseUrlThrow url
77
- mgr <- newManager defaultManagerSettings
78
- let baseReq = setRequestIgnoreStatus $ applyBasicAuth user pass url'
79
- { method = " POST"
80
- , requestHeaders = [(hContentType, " application/json" )] }
81
- return $ \ r -> do
82
- resp <- httpLbs (baseReq { requestBody = RequestBodyLBS r }) mgr
83
- return $ responseBody resp
81
+ url' <- parseUrlThrow url
82
+ mgr <- newManager defaultManagerSettings
83
+ let baseReq =
84
+ setRequestIgnoreStatus $
85
+ applyBasicAuth
86
+ user
87
+ pass
88
+ url'
89
+ { method = " POST" ,
90
+ requestHeaders = [(hContentType, " application/json" )]
91
+ }
92
+ return $ \ r -> do
93
+ resp <- httpLbs (baseReq {requestBody = RequestBodyLBS r}) mgr
94
+ return $ responseBody resp
84
95
85
96
-- | 'callApi' is a low-level interface for making authenticated API
86
97
-- calls to a Bitcoin daemon. The first argument specifies
@@ -94,54 +105,60 @@ getClient url user pass = do
94
105
-- callApi client "getblockhash" [tj 0]
95
106
--
96
107
-- On error, throws a 'BitcoinException'.
97
- callApi :: FromJSON v
98
- => Client -- ^ RPC client for bitcoind
99
- -> Text -- ^ command name
100
- -> [Value ] -- ^ command arguments
101
- -> IO v
108
+ callApi ::
109
+ (FromJSON v ) =>
110
+ -- | RPC client for bitcoind
111
+ Client ->
112
+ -- | command name
113
+ Text ->
114
+ -- | command arguments
115
+ [Value ] ->
116
+ IO v
102
117
callApi client cmd params = readVal =<< client jsonRpcReqBody
103
- where
104
- readVal bs = do
105
- case decode' bs of
106
- Just (BitcoinRpcResult r)
107
- -> return r
108
- Just (BitcoinRpcError (BitcoinRpcFailure code msg))
109
- -> throw $ BitcoinApiError code msg
110
- Nothing
111
- -> throw $ BitcoinResultTypeError bs
112
- jsonRpcReqBody =
113
- encode $ object [ " jsonrpc" .= (" 2.0" :: Text )
114
- , " method" .= cmd
115
- , " params" .= params
116
- , " id" .= (1 :: Int )
117
- ]
118
+ where
119
+ readVal bs = do
120
+ case decode' bs of
121
+ Just (BitcoinRpcResult r) ->
122
+ return r
123
+ Just (BitcoinRpcError (BitcoinRpcFailure code msg)) ->
124
+ throw $ BitcoinApiError code msg
125
+ Nothing ->
126
+ throw $ BitcoinResultTypeError bs
127
+ jsonRpcReqBody =
128
+ encode $
129
+ object
130
+ [ " jsonrpc" .= (" 2.0" :: Text ),
131
+ " method" .= cmd,
132
+ " params" .= params,
133
+ " id" .= (1 :: Int )
134
+ ]
118
135
{-# INLINE callApi #-}
119
136
120
137
-- | Used to allow "null" to decode to a tuple.
121
- newtype Nil = Nil { unNil :: () }
138
+ newtype Nil = Nil { unNil :: () }
122
139
123
140
instance FromJSON Nil where
124
- parseJSON Null = return $ Nil ()
125
- parseJSON x = fail $ " \" null\" was expected, but " ++ show x ++ " was recieved."
141
+ parseJSON Null = return $ Nil ()
142
+ parseJSON x = fail $ " \" null\" was expected, but " ++ show x ++ " was recieved."
126
143
127
144
-- | Used to parse "null" or [HexString]
128
145
newtype NilOrArray = NilOrArray { unArr :: Maybe [HexString ]}
129
146
130
147
instance FromJSON NilOrArray where
131
- parseJSON Null = return $ NilOrArray Nothing
132
- parseJSON a@ (Array _) = NilOrArray <$> parseJSON a
133
- parseJSON x = fail $ " Expected \" null\" or array, but " ++ show x ++ " was recieved."
148
+ parseJSON Null = return $ NilOrArray Nothing
149
+ parseJSON a@ (Array _) = NilOrArray <$> parseJSON a
150
+ parseJSON x = fail $ " Expected \" null\" or array, but " ++ show x ++ " was recieved."
134
151
135
152
-- | A handy shortcut for toJSON, because I'm lazy.
136
- tj :: ToJSON a => a -> Value
153
+ tj :: ( ToJSON a ) => a -> Value
137
154
tj = toJSON
138
155
{-# INLINE tj #-}
139
156
140
- tjm :: ToJSON a => a -> Maybe a -> Value
157
+ tjm :: ( ToJSON a ) => a -> Maybe a -> Value
141
158
tjm d m = tj $ fromMaybe d m
142
159
{-# INLINE tjm #-}
143
160
144
- tja :: ToJSON a => Maybe a -> [Value ]
161
+ tja :: ( ToJSON a ) => Maybe a -> [Value ]
145
162
tja = maybe [] (pure . tj)
146
163
{-# INLINE tja #-}
147
164
@@ -151,4 +168,7 @@ tja = maybe [] (pure . tj)
151
168
newtype AddrAddress = AA (Vector (Address , BTC ))
152
169
153
170
instance ToJSON AddrAddress where
154
- toJSON (AA vec) = object . V. toList $ uncurry (.=) <$> vec
171
+ toJSON (AA vec) =
172
+ object
173
+ . V. toList
174
+ $ fmap (\ (k, v) -> AK. fromText k .= v) vec
0 commit comments