mirror of https://github.com/voidlizard/hbs2
189 lines
6.4 KiB
Haskell
189 lines
6.4 KiB
Haskell
{-# Language AllowAmbiguousTypes #-}
|
|
{-# Language UndecidableInstances #-}
|
|
module HBS2.Peer.RPC.Client.RefChan where
|
|
|
|
import HBS2.OrDie
|
|
import HBS2.Storage
|
|
import HBS2.Merkle
|
|
import HBS2.Storage.Operations.ByteString
|
|
import HBS2.Data.Types.SignedBox
|
|
|
|
import HBS2.Peer.Proto.RefChan
|
|
import HBS2.Peer.Prelude
|
|
import HBS2.Peer.RPC.API.RefChan
|
|
import HBS2.Peer.RPC.API.Storage
|
|
import HBS2.Peer.RPC.Client.Internal
|
|
import HBS2.Peer.RPC.Client.StorageClient
|
|
|
|
import Data.ByteString (ByteString)
|
|
import Data.Coerce
|
|
import Control.Monad.Except
|
|
import Control.Monad.Trans.Maybe
|
|
import Control.Monad.Trans.Cont
|
|
import Control.Monad.Reader
|
|
import Codec.Serialise
|
|
import UnliftIO
|
|
|
|
|
|
getRefChanHeadHash :: forall proto m . ( MonadUnliftIO m
|
|
, HasClientAPI RefChanAPI proto m
|
|
, HasProtocol proto (ServiceProto RefChanAPI proto)
|
|
)
|
|
=> PubKey 'Sign 'HBS2Basic
|
|
-> m (Maybe HashRef)
|
|
getRefChanHeadHash puk = do
|
|
api <- getClientAPI @RefChanAPI @proto
|
|
callRpcWaitMay @RpcRefChanHeadGet (TimeoutSec 1) api puk >>= \case
|
|
Nothing -> throwIO RpcTimeoutError
|
|
Just e -> pure e
|
|
|
|
|
|
getRefChanHead :: forall proto m . ( MonadUnliftIO m
|
|
, HasClientAPI RefChanAPI proto m
|
|
, HasClientAPI StorageAPI proto m
|
|
, HasProtocol proto (ServiceProto RefChanAPI proto)
|
|
, HasProtocol proto (ServiceProto StorageAPI proto)
|
|
)
|
|
=> PubKey 'Sign 'HBS2Basic
|
|
-> m (Maybe (RefChanHeadBlock L4Proto))
|
|
getRefChanHead puk = do
|
|
|
|
sto <- getClientAPI @StorageAPI @proto <&> AnyStorage . StorageClient
|
|
|
|
runMaybeT do
|
|
hx <- lift (getRefChanHeadHash @proto puk) >>= toMPlus
|
|
lbs <- runExceptT (readFromMerkle sto (SimpleKey (coerce hx)))
|
|
>>= orThrowPassIO
|
|
|
|
-- FIXME: error-on-bad-signature
|
|
(_, hdblk) <- unboxSignedBox @(RefChanHeadBlock L4Proto) @'HBS2Basic lbs
|
|
& toMPlus
|
|
|
|
pure hdblk
|
|
|
|
postRefChanTx :: forall proto s m . ( MonadUnliftIO m
|
|
, HasClientAPI RefChanAPI proto m
|
|
, HasClientAPI StorageAPI proto m
|
|
, HasProtocol proto (ServiceProto RefChanAPI proto)
|
|
, HasProtocol proto (ServiceProto StorageAPI proto)
|
|
, ForSignedBox s
|
|
, s ~ HBS2Basic
|
|
)
|
|
=> PubKey 'Sign s
|
|
-> SignedBox ByteString s
|
|
-> m ()
|
|
postRefChanTx puk box = do
|
|
api <- getClientAPI @RefChanAPI @proto
|
|
callRpcWaitMay @RpcRefChanPropose (TimeoutSec 1) api (puk, box) >>= \case
|
|
Nothing -> throwIO RpcTimeoutError
|
|
Just e -> pure e
|
|
|
|
fetchRefChanHead :: forall proto m . ( MonadUnliftIO m
|
|
, HasClientAPI RefChanAPI proto m
|
|
, HasProtocol proto (ServiceProto RefChanAPI proto)
|
|
)
|
|
=> PubKey 'Sign 'HBS2Basic
|
|
-> m ()
|
|
fetchRefChanHead puk = do
|
|
api <- getClientAPI @RefChanAPI @proto
|
|
callRpcWaitMay @RpcRefChanHeadFetch (TimeoutSec 1) api puk >>= \case
|
|
Nothing -> throwIO RpcTimeoutError
|
|
_ -> pure ()
|
|
|
|
fetchRefChan :: forall proto m . ( MonadUnliftIO m
|
|
, HasClientAPI RefChanAPI proto m
|
|
, HasProtocol proto (ServiceProto RefChanAPI proto)
|
|
)
|
|
=> PubKey 'Sign 'HBS2Basic
|
|
-> m ()
|
|
fetchRefChan puk = do
|
|
api <- getClientAPI @RefChanAPI @proto
|
|
callRpcWaitMay @RpcRefChanFetch (TimeoutSec 1) api puk >>= \case
|
|
Nothing -> throwIO RpcTimeoutError
|
|
_ -> pure ()
|
|
|
|
|
|
getRefChanValue :: forall proto m . ( MonadUnliftIO m
|
|
, HasClientAPI RefChanAPI proto m
|
|
, HasProtocol proto (ServiceProto RefChanAPI proto)
|
|
)
|
|
=> PubKey 'Sign 'HBS2Basic
|
|
-> m (Maybe HashRef)
|
|
getRefChanValue puk = do
|
|
api <- getClientAPI @RefChanAPI @proto
|
|
callRpcWaitMay @RpcRefChanGet (TimeoutSec 1) api puk >>= \case
|
|
Nothing -> throwIO RpcTimeoutError
|
|
Just e -> pure e
|
|
|
|
|
|
|
|
|
|
-- this is not MonadUnliftIO to be compatible with
|
|
-- streaming
|
|
--
|
|
|
|
data RScanEnv proto =
|
|
RScanEnv {
|
|
rchanAPI :: ServiceCaller RefChanAPI proto
|
|
}
|
|
|
|
|
|
instance Monad m => HasClientAPI RefChanAPI proto (ReaderT (RScanEnv proto) m) where
|
|
getClientAPI = asks rchanAPI
|
|
|
|
data RefChanUpdateUnpacked e =
|
|
A (AcceptTran e) | P HashRef (ProposeTran e)
|
|
deriving stock (Generic)
|
|
|
|
instance ForRefChans e => Serialise (RefChanUpdateUnpacked e)
|
|
|
|
{-# COMPLETE A,P #-}
|
|
|
|
walkRefChanTx :: forall proto m . ( MonadIO m
|
|
, HasClientAPI RefChanAPI proto m
|
|
, HasProtocol proto (ServiceProto RefChanAPI proto)
|
|
, HasStorage m
|
|
)
|
|
=> (HashRef -> m Bool)
|
|
-> PubKey 'Sign 'HBS2Basic
|
|
-> (HashRef -> RefChanUpdateUnpacked L4Proto -> m ())
|
|
-> m ()
|
|
walkRefChanTx filt puk action = do
|
|
sto <- getStorage
|
|
api <- getClientAPI @RefChanAPI @proto
|
|
|
|
let env = RScanEnv api
|
|
|
|
flip runContT pure $ callCC $ \exit -> do
|
|
|
|
rcv' <- liftIO (runReaderT (getRefChanValue @proto puk) env)
|
|
|
|
rcv <- ContT $ maybe1 rcv' none
|
|
|
|
walkMerkle (coerce rcv) (getBlock sto) $ \case
|
|
-- FIXME: error-handling
|
|
Left _ -> exit ()
|
|
|
|
Right (hs :: [HashRef]) -> do
|
|
for_ hs $ \h -> do
|
|
want <- lift (filt h)
|
|
when want do
|
|
lbs' <- getBlock sto (coerce h)
|
|
lbs <- ContT $ maybe1 lbs' none
|
|
|
|
let txraw = deserialiseOrFail @(RefChanUpdate L4Proto) lbs
|
|
& either (const Nothing) Just
|
|
|
|
tx <- ContT $ maybe1 txraw none
|
|
|
|
case tx of
|
|
|
|
Accept _ box -> do
|
|
(_, txx) <- ContT $ maybe1 (unboxSignedBox0 box) none
|
|
lift $ action h (A txx)
|
|
|
|
Propose _ box -> do
|
|
(_, txx) <- ContT $ maybe1 (unboxSignedBox0 box) none
|
|
lift $ action h (P h txx)
|
|
|