electrs-client-0.1.0.0: src/ElectrsClient/Rpc.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module ElectrsClient.Rpc
( getBalance,
version,
blockHeader,
Balance (..),
ScriptHash (..),
BlockHeader (..),
)
where
import Data.Aeson (decode, encode, withObject, (.:))
import qualified Data.ByteString.Lazy as BS
import qualified Data.Digest.Pure.SHA as SHA
import ElectrsClient.Client as Client
import ElectrsClient.Data.Env
import ElectrsClient.Import.External
import ElectrsClient.RpcRequest as Req
import ElectrsClient.RpcResponse as Resp
import ElectrsClient.Type
import qualified Network.Bitcoin.Wallet as BtcW
import qualified Text.Hex as TH
newtype ScriptHash = ScriptHash Text
deriving stock (Generic)
newtype BlockHeader = BlockHeader Text
deriving newtype (Eq)
deriving stock (Generic)
instance Out BlockHeader
newtype MSat = MSat Word64
deriving newtype
( Eq,
Num,
Ord,
FromJSON,
Show,
Read
)
deriving stock
( Generic
)
instance Out MSat
data Balance = Balance
{ confirmed :: MSat,
unconfirmed :: MSat
}
deriving stock (Generic, Show)
getBalanceFromSat :: MSat -> MSat -> Balance
getBalanceFromSat c u = Balance (c * 1000) (u * 1000)
instance FromJSON Balance where
parseJSON = withObject "Balance" $ \v ->
getBalanceFromSat
<$> v .: "confirmed"
<*> v .: "unconfirmed"
instance ToJSON ScriptHash
instance FromJSON BlockHeader
instance Out Balance
callRpc :: (MonadUnliftIO m, ToJSON req, FromJSON resp) => Method -> req -> ElectrsEnv -> m (Either RpcError resp)
callRpc m r env = do
let request =
RpcRequest
{ Req.id = 0,
Req.jsonrpc = "2.0",
Req.method = m,
Req.params = r
}
msgReply <- Client.send (lazyEncode request) env
pure $ join $ second (second result . lazyDecode) msgReply
where
lazyDecode :: FromJSON resp => ByteString -> Either RpcError resp
lazyDecode = maybeToRight RpcJsonDecodeError . decode . BS.fromStrict
lazyEncode :: ToJSON req => RpcRequest req -> ByteString
lazyEncode = BS.toStrict . encode
version :: MonadUnliftIO m => ElectrsEnv -> () -> m (Either RpcError [Text])
version env () = callRpc Version ["" :: Text, "1.4" :: Text] env
getBalance :: MonadUnliftIO m => ElectrsEnv -> BitcoindEnv -> Either (OnChainAddress a) ScriptHash -> m (Either RpcError Balance)
getBalance env _ (Right scriptHash) = callRpc GetBalance [scriptHash] env
getBalance env bEnv (Left address) = do
scrHash <- getScriptHash bEnv address
case scrHash of
Right sh -> getBalance env bEnv (Right sh)
Left _ -> pure $ Left (OtherError "Getting ScriptHash error")
blockHeader :: MonadUnliftIO m => ElectrsEnv -> BlkHeight -> m (Either RpcError BlockHeader)
blockHeader env bh = callRpc GetBlockHeader [bh] env
getScriptHash :: (MonadUnliftIO m) => BitcoindEnv -> OnChainAddress a -> m (Either RpcError ScriptHash)
getScriptHash bEnv addr = runExceptT $ do
btcClient <-
liftIO $
BtcW.getClient
(unpack $ bitcoindEnvHost bEnv)
(encodeUtf8 $ bitcoindEnvUsername bEnv)
(encodeUtf8 $ bitcoindEnvPassword bEnv)
BtcW.AddrInfo _ sp _ _ <- liftIO $ BtcW.getAddrInfo btcClient (coerce addr)
decodeSp sp
where
decodeSp :: (MonadUnliftIO m) => BtcW.ScrPubKey -> ExceptT RpcError m ScriptHash
decodeSp =
ExceptT
. pure
. second sha256AndReverse
. maybeToRight RpcHexDecodeError
. TH.decodeHex
. coerce
sha256AndReverse =
ScriptHash
. TH.encodeHex
. BS.toStrict
. BS.reverse
. SHA.bytestringDigest
. SHA.sha256
. BS.fromStrict