bitcoind-rpc-0.2.0.0: src/Bitcoin/Core/RPC/Network.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Bitcoin.Core.RPC.Network (
Command (..),
addNode,
clearBanned,
disconnectNode,
NodeInfo (..),
NodeInfoAddress (..),
ConnDir (..),
getAddedNodeInfo,
getConnectionCount,
NetTotals (..),
getNetTotals,
NodeAddress (..),
getNodeAddresses,
PeerInfo (..),
getPeerInfo,
listBanned,
) where
import Data.Aeson (
FromJSON (..),
ToJSON (..),
withObject,
withText,
(.:),
)
import Data.Proxy (Proxy (..))
import Data.Text (Text)
import Data.Time (NominalDiffTime, UTCTime)
import Data.Word (Word16, Word32, Word64)
import Haskoin.Block (BlockHeight)
import Servant.API ((:<|>) (..))
import Servant.Bitcoind (
BitcoindClient,
BitcoindEndpoint,
C,
CX,
I,
O,
toBitcoindClient,
toSatoshis,
utcTime,
)
-- | Commands as understood by 'addNode'
data Command = Add | Remove | OneTry deriving (Eq, Show, Enum)
commandText :: Command -> Text
commandText = \case
Add -> "add"
Remove -> "remove"
OneTry -> "onetry"
instance ToJSON Command where
toJSON = toJSON . commandText
data NodeAddress = NodeAddress
{ addrTime :: UTCTime
, addrServices :: Word64
, addrHost :: Text
, addrPort :: Word32
}
deriving (Eq, Show)
instance FromJSON NodeAddress where
parseJSON = withObject "NodeAddress" $ \o ->
NodeAddress
<$> (utcTime <$> o .: "time")
<*> o .: "services"
<*> o .: "address"
<*> o .: "port"
data ConnDir = Inbound | Outbound deriving (Eq, Show, Enum)
instance FromJSON ConnDir where
parseJSON = withText "ConnDir" fromText
where
fromText t
| t == "inbound" = return Inbound
| t == "outbound" = return Outbound
| otherwise = fail "Unable to decode connection direction"
data NodeInfoAddress = NodeInfoAddress
{ nodeInfoAddress :: Text
, connDirection :: ConnDir
}
deriving (Eq, Show)
instance FromJSON NodeInfoAddress where
parseJSON = withObject "NodeInfoAddress" $ \o ->
NodeInfoAddress <$> o .: "address" <*> o .: "connected"
data NodeInfo = NodeInfo
{ addedNode :: Text
, connected :: Bool
, addresses :: [NodeInfoAddress]
}
deriving (Eq, Show)
instance FromJSON NodeInfo where
parseJSON = withObject "NodeInfo" $ \o ->
NodeInfo <$> o .: "addednode" <*> o .: "connected" <*> o .: "addresses"
data NetTotals = NetTotals
{ bytesReceived :: Word64
, bytesSent :: Word64
}
deriving (Eq, Show)
instance FromJSON NetTotals where
parseJSON = withObject "NetTotals" $ \o ->
NetTotals <$> o .: "totalbytesrecv" <*> o .: "totalbytessent"
data PeerInfo = PeerInfo
{ peerIndex :: Word16
, peerAddr :: Text
, peerBind :: Text
, services :: Text
, relay :: Bool
, lastSend :: UTCTime
, lastRecv :: UTCTime
, peerBytesSent :: Word64
, peerBytesRecv :: Word64
, connTime :: UTCTime
, timeOffset :: NominalDiffTime
, pingTime :: Maybe Double
, version :: Word64
, inbound :: Bool
, addnode :: Bool
, startingHeight :: BlockHeight
, banScore :: Word16
, syncedHeaders :: Word32
, syncedBlocks :: Word32
, inflight :: [BlockHeight]
, whitelisted :: Bool
, -- | in satoshis
minFeeFilter :: Word32
}
deriving (Eq, Show)
instance FromJSON PeerInfo where
parseJSON = withObject "PeerInfo" $ \o ->
PeerInfo
<$> o .: "id"
<*> o .: "addr"
<*> o .: "addrbind"
<*> o .: "services"
<*> o .: "relaytxes"
<*> (utcTime <$> o .: "lastsend")
<*> (utcTime <$> o .: "lastrecv")
<*> o .: "bytessent"
<*> o .: "bytesrecv"
<*> (utcTime <$> o .: "conntime")
<*> (fromIntegral @Int <$> o .: "timeoffset")
<*> o .: "pingtime"
<*> o .: "version"
<*> o .: "inbound"
<*> o .: "addnode"
<*> o .: "startingheight"
<*> o .: "banscore"
<*> o .: "synced_headers"
<*> o .: "synced_blocks"
<*> o .: "inflight"
<*> o .: "whitelisted"
<*> (toSatoshis <$> o .: "minfeefilter")
type NetworkRpc =
BitcoindEndpoint "addnode" (I Text -> I Command -> CX)
:<|> BitcoindEndpoint "clearbanned" CX
:<|> BitcoindEndpoint "disconnectnode" (I Text -> CX)
:<|> BitcoindEndpoint "getaddednodeinfo" (O Text -> C [NodeInfo])
:<|> BitcoindEndpoint "getconnectioncount" (C Word16)
:<|> BitcoindEndpoint "getnettotals" (C NetTotals)
:<|> BitcoindEndpoint "getnodeaddresses" (O Word32 -> C [NodeAddress])
:<|> BitcoindEndpoint "getpeerinfo" (C [PeerInfo])
:<|> BitcoindEndpoint "listbanned" (C [Text])
{- | Attempts to add or remove a node from the addnode list; or try a
connection to a node once. Nodes added using addnode are protected from DoS
disconnection and are not required to be full nodes/support SegWit as other
outbound peers are (though such peers will not be synced from).
-}
addNode ::
-- | node address @host:port@
Text ->
Command ->
BitcoindClient ()
-- | Clear all banned IPs.
clearBanned :: BitcoindClient ()
-- | Immediately disconnects from the specified peer node.
disconnectNode ::
-- | node address @host:port@
Text ->
BitcoindClient ()
{- | Returns information about the given added node, or all added nodes (note
that onetry addnodes are not listed here)
-}
getAddedNodeInfo ::
-- | optionally specify a node by address
Maybe Text ->
BitcoindClient [NodeInfo]
-- | Returns the number of connections to other nodes.
getConnectionCount :: BitcoindClient Word16
{- | Returns information about network traffic, including bytes in, bytes out,
and current time.
-}
getNetTotals :: BitcoindClient NetTotals
{- | Return known addresses which can potentially be used to find new nodes in
the network
-}
getNodeAddresses :: Maybe Word32 -> BitcoindClient [NodeAddress]
-- | Returns data about each connected network node.
getPeerInfo :: BitcoindClient [PeerInfo]
-- | List all banned IPs/Subnets.
listBanned :: BitcoindClient [Text]
addNode
:<|> clearBanned
:<|> disconnectNode
:<|> getAddedNodeInfo
:<|> getConnectionCount
:<|> getNetTotals
:<|> getNodeAddresses
:<|> getPeerInfo
:<|> listBanned =
toBitcoindClient $ Proxy @NetworkRpc