mirror of https://github.com/voidlizard/hbs2
452 lines
15 KiB
Haskell
452 lines
15 KiB
Haskell
{-# Language TemplateHaskell #-}
|
|
{-# Language UndecidableInstances #-}
|
|
module RPC where
|
|
|
|
|
|
import HBS2.Actors.Peer
|
|
import HBS2.Base58
|
|
import HBS2.Clock
|
|
import HBS2.Hash
|
|
import HBS2.Net.Auth.Credentials
|
|
import HBS2.Net.IP.Addr
|
|
import HBS2.Net.Messaging.UDP
|
|
import HBS2.Net.Proto
|
|
import HBS2.Net.Proto.Definition()
|
|
import HBS2.OrDie
|
|
import HBS2.Prelude.Plated
|
|
import HBS2.System.Logger.Simple hiding (info)
|
|
import HBS2.System.Logger.Simple qualified as Log
|
|
|
|
import PeerConfig
|
|
|
|
|
|
import Control.Monad.IO.Unlift
|
|
import Codec.Serialise (serialise,deserialiseOrFail)
|
|
import Control.Applicative
|
|
import Control.Concurrent.STM
|
|
import Control.Concurrent.STM.TQueue
|
|
import Control.Monad.Reader
|
|
import Control.Monad.Trans.Resource
|
|
import Data.ByteString.Lazy (ByteString)
|
|
import Data.Function
|
|
import Data.Functor
|
|
import Data.List qualified as L
|
|
import Lens.Micro.Platform
|
|
import Network.Socket
|
|
import System.Exit
|
|
import System.IO
|
|
import UnliftIO.Async as U
|
|
import Control.Concurrent.MVar
|
|
|
|
data PeerRpcKey
|
|
|
|
instance HasCfgKey PeerRpcKey (Maybe String) where
|
|
key = "rpc"
|
|
|
|
data SetLogging =
|
|
DebugOn Bool
|
|
| TraceOn Bool
|
|
deriving (Generic,Eq,Show)
|
|
|
|
instance Serialise SetLogging
|
|
|
|
data RPCCommand =
|
|
DIE
|
|
| POKE
|
|
| ANNOUNCE (Hash HbSync)
|
|
| PING (PeerAddr L4Proto) (Maybe (Peer L4Proto))
|
|
| CHECK PeerNonce (PeerAddr L4Proto) (Hash HbSync)
|
|
| FETCH (Hash HbSync)
|
|
| PEERS
|
|
| PEXINFO
|
|
| SETLOG SetLogging
|
|
| REFLOGUPDATE ByteString
|
|
| REFLOGFETCH (PubKey 'Sign (Encryption L4Proto))
|
|
| REFLOGGET (PubKey 'Sign (Encryption L4Proto))
|
|
| REFCHANHEADSEND (Hash HbSync)
|
|
| REFCHANHEADGET (PubKey 'Sign (Encryption L4Proto))
|
|
| REFCHANHEADFETCH (PubKey 'Sign (Encryption L4Proto))
|
|
| REFCHANFETCH (PubKey 'Sign (Encryption L4Proto))
|
|
| REFCHANGET (PubKey 'Sign (Encryption L4Proto))
|
|
| REFCHANPROPOSE (PubKey 'Sign (Encryption L4Proto), ByteString)
|
|
| REFCHANNOTIFY (PubKey 'Sign (Encryption L4Proto), ByteString)
|
|
|
|
data RPC e =
|
|
RPCDie
|
|
| RPCPoke
|
|
| RPCPing (PeerAddr e)
|
|
| RPCPong (PeerAddr e)
|
|
| RPCPokeAnswer (PubKey 'Sign (Encryption e))
|
|
| RPCPokeAnswerFull Text
|
|
| RPCAnnounce (Hash HbSync)
|
|
| RPCFetch (Hash HbSync)
|
|
| RPCPeers
|
|
| RPCPeersAnswer (PeerAddr e) (PubKey 'Sign (Encryption e))
|
|
| RPCPexInfo
|
|
| RPCPexInfoAnswer [PeerAddr L4Proto]
|
|
| RPCLogLevel SetLogging
|
|
| RPCRefLogUpdate ByteString
|
|
| RPCRefLogFetch (PubKey 'Sign (Encryption e))
|
|
| RPCRefLogGet (PubKey 'Sign (Encryption e))
|
|
| RPCRefLogGetAnswer (Maybe (Hash HbSync))
|
|
|
|
| RPCRefChanHeadSend (Hash HbSync)
|
|
| RPCRefChanHeadGet (PubKey 'Sign (Encryption e))
|
|
| RPCRefChanHeadGetAnsw (Maybe (Hash HbSync))
|
|
| RPCRefChanHeadFetch (PubKey 'Sign (Encryption e))
|
|
|
|
| RPCRefChanFetch (PubKey 'Sign (Encryption e))
|
|
| RPCRefChanGet (PubKey 'Sign (Encryption e))
|
|
| RPCRefChanGetAnsw (Maybe (Hash HbSync))
|
|
|
|
| RPCRefChanPropose (PubKey 'Sign (Encryption e), ByteString)
|
|
| RPCRefChanNotify (PubKey 'Sign (Encryption e), ByteString)
|
|
|
|
deriving stock (Generic)
|
|
|
|
deriving instance
|
|
( Show (PubKey 'Sign (Encryption e))
|
|
, Show (PeerAddr e)
|
|
) => Show (RPC e)
|
|
|
|
instance (Serialise (PeerAddr e), Serialise (PubKey 'Sign (Encryption e))) => Serialise (RPC e)
|
|
|
|
instance HasProtocol L4Proto (RPC L4Proto) where
|
|
type instance ProtocolId (RPC L4Proto) = 0xFFFFFFE0
|
|
type instance Encoded L4Proto = ByteString
|
|
decode = either (const Nothing) Just . deserialiseOrFail
|
|
encode = serialise
|
|
|
|
|
|
data RPCEnv =
|
|
RPCEnv
|
|
{ _rpcSelf :: Peer L4Proto
|
|
, _rpcFab :: Fabriq L4Proto
|
|
}
|
|
|
|
makeLenses 'RPCEnv
|
|
|
|
data RpcAdapter e m =
|
|
RpcAdapter
|
|
{ rpcOnPoke :: RPC e -> m ()
|
|
, rpcOnDie :: RPC e -> m ()
|
|
, rpcOnPokeAnswer :: PubKey 'Sign (Encryption e) -> m ()
|
|
, rpcOnPokeAnswerFull :: Text -> m ()
|
|
, rpcOnAnnounce :: Hash HbSync -> m ()
|
|
, rpcOnPing :: PeerAddr e -> m ()
|
|
, rpcOnPong :: PeerAddr e -> m ()
|
|
, rpcOnFetch :: Hash HbSync -> m ()
|
|
, rpcOnPeers :: RPC e -> m ()
|
|
, rpcOnPeersAnswer :: (PeerAddr e, PubKey 'Sign (Encryption e)) -> m ()
|
|
, rpcOnPexInfo :: RPC e -> m ()
|
|
, rpcOnPexInfoAnswer :: [PeerAddr L4Proto] -> m ()
|
|
, rpcOnLogLevel :: SetLogging -> m ()
|
|
, rpcOnRefLogUpdate :: ByteString -> m ()
|
|
, rpcOnRefLogFetch :: PubKey 'Sign (Encryption e) -> m ()
|
|
, rpcOnRefLogGet :: PubKey 'Sign (Encryption e) -> m ()
|
|
, rpcOnRefLogGetAnsw :: Maybe (Hash HbSync) -> m ()
|
|
|
|
, rpcOnRefChanHeadSend :: Hash HbSync -> m ()
|
|
, rpcOnRefChanHeadGet :: PubKey 'Sign (Encryption e) -> m ()
|
|
, rpcOnRefChanHeadGetAnsw :: Maybe (Hash HbSync) -> m ()
|
|
, rpcOnRefChanHeadFetch :: PubKey 'Sign (Encryption e) -> m ()
|
|
|
|
-- refchan commands
|
|
, rpcOnRefChanFetch :: PubKey 'Sign (Encryption e) -> m ()
|
|
, rpcOnRefChanGet :: PubKey 'Sign (Encryption e) -> m ()
|
|
, rpcOnRefChanGetAnsw :: Maybe (Hash HbSync) -> m ()
|
|
|
|
, rpcOnRefChanPropose :: (PubKey 'Sign (Encryption e), ByteString) -> m ()
|
|
, rpcOnRefChanNotify :: (PubKey 'Sign (Encryption e), ByteString) -> m ()
|
|
}
|
|
|
|
newtype RpcM m a = RpcM { fromRpcM :: ReaderT RPCEnv m a }
|
|
deriving newtype ( Functor
|
|
, Applicative
|
|
, Monad
|
|
, MonadIO
|
|
, MonadReader RPCEnv
|
|
, MonadTrans
|
|
, MonadUnliftIO
|
|
)
|
|
|
|
runRPC :: ( MonadIO m
|
|
, PeerMessaging L4Proto
|
|
)
|
|
=> MessagingUDP -> RpcM m a -> m a
|
|
|
|
runRPC udp m = runReaderT (fromRpcM m) (RPCEnv pip (Fabriq udp))
|
|
where
|
|
pip = getOwnPeer udp
|
|
|
|
continueWithRPC :: RPCEnv -> RpcM m a -> m a
|
|
continueWithRPC e m = runReaderT (fromRpcM m) e
|
|
|
|
instance Monad m => HasFabriq L4Proto (RpcM m) where
|
|
getFabriq = asks (view rpcFab)
|
|
|
|
instance Monad m => HasOwnPeer L4Proto (RpcM m) where
|
|
ownPeer = asks (view rpcSelf)
|
|
|
|
instance (Monad m, HasProtocol L4Proto p) => HasTimeLimits L4Proto p (RpcM m) where
|
|
tryLockForPeriod _ _ = pure True
|
|
|
|
rpcHandler :: forall e m . ( MonadIO m
|
|
, Response e (RPC e) m
|
|
, HasProtocol e (RPC e)
|
|
, IsPeerAddr e m
|
|
)
|
|
=> RpcAdapter e m -> RPC e -> m ()
|
|
|
|
rpcHandler adapter = \case
|
|
p@RPCDie{} -> rpcOnDie adapter p
|
|
p@RPCPoke{} -> rpcOnPoke adapter p
|
|
(RPCPokeAnswer k) -> rpcOnPokeAnswer adapter k
|
|
(RPCPokeAnswerFull k) -> rpcOnPokeAnswerFull adapter k
|
|
(RPCAnnounce h) -> rpcOnAnnounce adapter h
|
|
(RPCPing pa) -> rpcOnPing adapter pa
|
|
(RPCPong pa) -> rpcOnPong adapter pa
|
|
(RPCFetch h) -> rpcOnFetch adapter h
|
|
p@RPCPeers{} -> rpcOnPeers adapter p
|
|
(RPCPeersAnswer pa k) -> rpcOnPeersAnswer adapter (pa,k)
|
|
p@RPCPexInfo{} -> rpcOnPexInfo adapter p
|
|
(RPCPexInfoAnswer pa) -> rpcOnPexInfoAnswer adapter pa
|
|
(RPCLogLevel l) -> rpcOnLogLevel adapter l
|
|
(RPCRefLogUpdate bs) -> rpcOnRefLogUpdate adapter bs
|
|
(RPCRefLogFetch e) -> rpcOnRefLogFetch adapter e
|
|
(RPCRefLogGet e) -> rpcOnRefLogGet adapter e
|
|
(RPCRefLogGetAnswer s) -> rpcOnRefLogGetAnsw adapter s
|
|
(RPCRefChanHeadSend s) -> rpcOnRefChanHeadSend adapter s
|
|
|
|
(RPCRefChanHeadGet s) -> rpcOnRefChanHeadGet adapter s
|
|
(RPCRefChanHeadGetAnsw s) -> rpcOnRefChanHeadGetAnsw adapter s
|
|
(RPCRefChanHeadFetch s) -> rpcOnRefChanHeadFetch adapter s
|
|
|
|
(RPCRefChanGet s) -> rpcOnRefChanGet adapter s
|
|
(RPCRefChanGetAnsw s) -> rpcOnRefChanGetAnsw adapter s
|
|
(RPCRefChanFetch s) -> rpcOnRefChanFetch adapter s
|
|
|
|
(RPCRefChanPropose s) -> rpcOnRefChanPropose adapter s
|
|
(RPCRefChanNotify s) -> rpcOnRefChanNotify adapter s
|
|
|
|
data RPCOpt =
|
|
RPCOpt
|
|
{ _rpcOptConf :: Maybe FilePath
|
|
, _rpcOptAddr :: Maybe String
|
|
}
|
|
|
|
makeLenses 'RPCOpt
|
|
|
|
|
|
runRpcCommand :: FromStringMaybe (IPAddrPort L4Proto) => RPCOpt -> RPCCommand -> IO ()
|
|
runRpcCommand opt = \case
|
|
DIE -> withRPC opt RPCDie
|
|
POKE -> withRPC opt RPCPoke
|
|
PING s _ -> withRPC opt (RPCPing s)
|
|
ANNOUNCE h -> withRPC opt (RPCAnnounce h)
|
|
FETCH h -> withRPC opt (RPCFetch h)
|
|
PEERS -> withRPC opt RPCPeers
|
|
|
|
PEXINFO -> withRPC opt RPCPexInfo
|
|
|
|
SETLOG s -> withRPC opt (RPCLogLevel s)
|
|
REFLOGUPDATE bs -> withRPC opt (RPCRefLogUpdate bs)
|
|
REFLOGFETCH k -> withRPC opt (RPCRefLogFetch k)
|
|
REFLOGGET k -> withRPC opt (RPCRefLogGet k)
|
|
|
|
REFCHANHEADSEND h -> withRPC opt (RPCRefChanHeadSend h)
|
|
REFCHANHEADGET s -> withRPC opt (RPCRefChanHeadGet s)
|
|
REFCHANHEADFETCH s -> withRPC opt (RPCRefChanHeadFetch s)
|
|
|
|
REFCHANGET s -> withRPC opt (RPCRefChanGet s)
|
|
REFCHANFETCH s -> withRPC opt (RPCRefChanFetch s)
|
|
|
|
REFCHANPROPOSE s -> withRPC opt (RPCRefChanPropose s)
|
|
REFCHANNOTIFY s -> withRPC opt (RPCRefChanNotify s)
|
|
|
|
_ -> pure ()
|
|
|
|
|
|
withRPC :: FromStringMaybe (PeerAddr L4Proto) => RPCOpt -> RPC L4Proto -> IO ()
|
|
withRPC o cmd = rpcClientMain o $ runResourceT do
|
|
|
|
liftIO $ hSetBuffering stdout LineBuffering
|
|
|
|
conf <- peerConfigRead (view rpcOptConf o)
|
|
|
|
let rpcConf = cfgValue @PeerRpcKey conf :: Maybe String
|
|
|
|
saddr <- pure (view rpcOptAddr o <|> rpcConf) `orDie` "RPC endpoint not set"
|
|
|
|
as <- liftIO $ parseAddrUDP (fromString saddr) <&> fmap (fromSockAddr @'UDP . addrAddress)
|
|
let rpc' = headMay $ L.sortBy (compare `on` addrPriority) as
|
|
|
|
rpc <- pure rpc' `orDie` "Can't parse RPC endpoint"
|
|
|
|
udp1 <- newMessagingUDP False Nothing `orDie` "Can't start RPC"
|
|
|
|
mrpc <- async $ runMessagingUDP udp1
|
|
|
|
pingQ <- liftIO newTQueueIO
|
|
|
|
pokeQ <- liftIO newTQueueIO
|
|
|
|
pokeFQ <- liftIO newTQueueIO
|
|
|
|
refQ <- liftIO newTQueueIO
|
|
|
|
rchanheadMVar <- liftIO newEmptyMVar
|
|
|
|
rchangetMVar <- liftIO newEmptyMVar
|
|
|
|
let adapter = RpcAdapter
|
|
{ rpcOnPoke = dontHandle
|
|
, rpcOnDie = dontHandle
|
|
, rpcOnPokeAnswer = (liftIO . atomically . writeTQueue pokeQ)
|
|
, rpcOnPokeAnswerFull = (liftIO . atomically . writeTQueue pokeFQ)
|
|
, rpcOnAnnounce = (const $ liftIO exitSuccess)
|
|
, rpcOnPing = (const $ notice "ping?")
|
|
, rpcOnPong = (liftIO . atomically . writeTQueue pingQ)
|
|
, rpcOnFetch = dontHandle
|
|
, rpcOnPeers = dontHandle
|
|
, rpcOnPeersAnswer = (\(pa, k) -> Log.info $ pretty (AsBase58 k) <+> pretty pa)
|
|
, rpcOnPexInfo = dontHandle
|
|
, rpcOnPexInfoAnswer = (\ps -> mapM_ (Log.info . pretty) ps)
|
|
, rpcOnLogLevel = dontHandle
|
|
, rpcOnRefLogUpdate = dontHandle
|
|
, rpcOnRefLogFetch = dontHandle
|
|
, rpcOnRefLogGet = dontHandle
|
|
, rpcOnRefLogGetAnsw = ( liftIO . atomically . writeTQueue refQ )
|
|
|
|
, rpcOnRefChanHeadSend = dontHandle
|
|
, rpcOnRefChanHeadGet = dontHandle
|
|
, rpcOnRefChanHeadGetAnsw = (liftIO . putMVar rchanheadMVar)
|
|
, rpcOnRefChanHeadFetch = dontHandle
|
|
|
|
, rpcOnRefChanFetch = dontHandle
|
|
, rpcOnRefChanGet = dontHandle
|
|
, rpcOnRefChanGetAnsw = (liftIO . putMVar rchangetMVar)
|
|
|
|
, rpcOnRefChanPropose = dontHandle
|
|
|
|
, rpcOnRefChanNotify = dontHandle
|
|
}
|
|
|
|
prpc <- async $ runRPC udp1 do
|
|
env <- ask
|
|
proto <- liftIO $ async $ continueWithRPC env $ do
|
|
runProto @L4Proto
|
|
[ makeResponse (rpcHandler adapter)
|
|
]
|
|
|
|
request rpc cmd
|
|
|
|
case cmd of
|
|
RPCAnnounce{} -> pause @'Seconds 0.1 >> liftIO exitSuccess
|
|
|
|
RPCFetch{} -> pause @'Seconds 0.1 >> liftIO exitSuccess
|
|
|
|
RPCPing{} -> do
|
|
void $ liftIO $ void $ race (pause @'Seconds 5 >> exitFailure) do
|
|
pa <- liftIO $ atomically $ readTQueue pingQ
|
|
Log.info $ "pong from" <+> pretty pa
|
|
exitSuccess
|
|
|
|
|
|
RPCDie{} -> do
|
|
pause @'Seconds 0.25
|
|
liftIO exitSuccess
|
|
|
|
RPCPoke{} -> do
|
|
let onTimeout = do pause @'Seconds 1.5
|
|
Log.info "no-one-is-here"
|
|
exitFailure
|
|
|
|
void $ liftIO $ race onTimeout do
|
|
k <- liftIO $ atomically $ readTQueue pokeFQ
|
|
print (pretty k)
|
|
hFlush stdout
|
|
exitSuccess
|
|
|
|
RPCPeers{} -> liftIO do
|
|
pause @'Seconds 1
|
|
exitSuccess
|
|
|
|
RPCPexInfo{} -> liftIO do
|
|
pause @'Seconds 1
|
|
exitSuccess
|
|
|
|
RPCLogLevel{} -> liftIO exitSuccess
|
|
|
|
RPCRefLogUpdate{} -> liftIO do
|
|
pause @'Seconds 0.1
|
|
exitSuccess
|
|
|
|
RPCRefLogFetch {} -> liftIO do
|
|
pause @'Seconds 0.5
|
|
exitSuccess
|
|
|
|
RPCRefLogGet{} -> liftIO do
|
|
void $ liftIO $ race (pause @'Seconds 0.1 >> exitFailure) do
|
|
k <- liftIO $ atomically $ readTQueue refQ
|
|
case k of
|
|
Nothing -> exitFailure
|
|
Just re -> do
|
|
print $ pretty re
|
|
hFlush stdout
|
|
exitSuccess
|
|
|
|
RPCRefChanHeadSend {} -> liftIO do
|
|
pause @'Seconds 0.25
|
|
exitSuccess
|
|
|
|
RPCRefChanHeadGet {} -> liftIO do
|
|
|
|
r <- race (pause @'Seconds 2) do
|
|
withMVar rchanheadMVar $ \v -> do
|
|
pure v
|
|
|
|
case r of
|
|
Right (Just x) -> print (pretty x) >> exitSuccess
|
|
|
|
_ -> exitFailure
|
|
|
|
RPCRefChanHeadFetch {} -> liftIO do
|
|
pause @'Seconds 0.25
|
|
exitSuccess
|
|
|
|
RPCRefChanFetch {} -> liftIO do
|
|
pause @'Seconds 0.25
|
|
exitSuccess
|
|
|
|
RPCRefChanGet {} -> liftIO do
|
|
r <- race (pause @'Seconds 2) do
|
|
withMVar rchangetMVar $ \v -> do
|
|
pure v
|
|
|
|
case r of
|
|
Right (Just x) -> print (pretty x) >> exitSuccess
|
|
|
|
_ -> exitFailure
|
|
|
|
RPCRefChanPropose{} -> liftIO do
|
|
pause @'Seconds 0.25
|
|
exitSuccess
|
|
|
|
RPCRefChanNotify{} -> liftIO do
|
|
pause @'Seconds 0.25
|
|
exitSuccess
|
|
|
|
_ -> pure ()
|
|
|
|
void $ liftIO $ waitAnyCancel [proto]
|
|
|
|
void $ waitAnyCancel [mrpc, prpc]
|
|
|
|
|
|
rpcClientMain :: RPCOpt -> IO () -> IO ()
|
|
rpcClientMain opt action = do
|
|
setLoggingOff @DEBUG
|
|
action
|
|
|