mirror of https://github.com/voidlizard/hbs2
peer announces and basic logging
This commit is contained in:
parent
16cdf223af
commit
41da76483c
|
@ -71,8 +71,8 @@ library
|
||||||
, HBS2.Clock
|
, HBS2.Clock
|
||||||
, HBS2.Data.Detect
|
, HBS2.Data.Detect
|
||||||
, HBS2.Data.Types
|
, HBS2.Data.Types
|
||||||
, HBS2.Data.Types.Refs
|
|
||||||
, HBS2.Data.Types.Crypto
|
, HBS2.Data.Types.Crypto
|
||||||
|
, HBS2.Data.Types.Refs
|
||||||
, HBS2.Defaults
|
, HBS2.Defaults
|
||||||
, HBS2.Events
|
, HBS2.Events
|
||||||
, HBS2.Hash
|
, HBS2.Hash
|
||||||
|
@ -89,13 +89,15 @@ library
|
||||||
, HBS2.Net.Proto.BlockChunks
|
, HBS2.Net.Proto.BlockChunks
|
||||||
, HBS2.Net.Proto.BlockInfo
|
, HBS2.Net.Proto.BlockInfo
|
||||||
, HBS2.Net.Proto.Definition
|
, HBS2.Net.Proto.Definition
|
||||||
, HBS2.Net.Proto.Sessions
|
|
||||||
, HBS2.Net.Proto.Peer
|
, HBS2.Net.Proto.Peer
|
||||||
|
, HBS2.Net.Proto.PeerAnnounce
|
||||||
|
, HBS2.Net.Proto.Sessions
|
||||||
, HBS2.Net.Proto.Types
|
, HBS2.Net.Proto.Types
|
||||||
, HBS2.OrDie
|
, HBS2.OrDie
|
||||||
, HBS2.Prelude
|
, HBS2.Prelude
|
||||||
, HBS2.Prelude.Plated
|
, HBS2.Prelude.Plated
|
||||||
, HBS2.Storage
|
, HBS2.Storage
|
||||||
|
, HBS2.System.Logger.Simple
|
||||||
|
|
||||||
|
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
|
@ -114,6 +116,7 @@ library
|
||||||
, cryptonite
|
, cryptonite
|
||||||
, deepseq
|
, deepseq
|
||||||
, directory
|
, directory
|
||||||
|
, fast-logger
|
||||||
, filelock
|
, filelock
|
||||||
, filepath
|
, filepath
|
||||||
, hashable
|
, hashable
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{-# Language TemplateHaskell #-}
|
{-# Language TemplateHaskell #-}
|
||||||
{-# Language UndecidableInstances #-}
|
{-# Language UndecidableInstances #-}
|
||||||
{-# Language FunctionalDependencies #-}
|
{-# Language FunctionalDependencies #-}
|
||||||
-- {-# Language AllowAmbiguousTypes #-}
|
{-# Language AllowAmbiguousTypes #-}
|
||||||
module HBS2.Actors.Peer where
|
module HBS2.Actors.Peer where
|
||||||
|
|
||||||
import HBS2.Actors
|
import HBS2.Actors
|
||||||
|
@ -21,6 +21,7 @@ import Control.Monad.Trans.Maybe
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Data.ByteString.Lazy (ByteString)
|
import Data.ByteString.Lazy (ByteString)
|
||||||
|
import Data.ByteString qualified as BS
|
||||||
import Data.Cache (Cache)
|
import Data.Cache (Cache)
|
||||||
import Data.Cache qualified as Cache
|
import Data.Cache qualified as Cache
|
||||||
import Data.Dynamic
|
import Data.Dynamic
|
||||||
|
@ -39,6 +40,7 @@ import Codec.Serialise (serialise, deserialiseOrFail)
|
||||||
|
|
||||||
import Prettyprinter hiding (pipe)
|
import Prettyprinter hiding (pipe)
|
||||||
|
|
||||||
|
|
||||||
data AnyStorage = forall zu . (Block ByteString ~ ByteString, Storage zu HbSync ByteString IO) => AnyStorage zu
|
data AnyStorage = forall zu . (Block ByteString ~ ByteString, Storage zu HbSync ByteString IO) => AnyStorage zu
|
||||||
|
|
||||||
instance (IsKey HbSync, Key HbSync ~ Hash HbSync, Block ByteString ~ ByteString) => Storage AnyStorage HbSync ByteString IO where
|
instance (IsKey HbSync, Key HbSync ~ Hash HbSync, Block ByteString ~ ByteString) => Storage AnyStorage HbSync ByteString IO where
|
||||||
|
@ -66,6 +68,8 @@ data Fabriq e = forall bus . (Messaging bus e (Encoded e)) => Fabriq bus
|
||||||
class HasFabriq e m where
|
class HasFabriq e m where
|
||||||
getFabriq :: m (Fabriq e)
|
getFabriq :: m (Fabriq e)
|
||||||
|
|
||||||
|
class HasPeerNonce e m where
|
||||||
|
peerNonce :: m PeerNonce
|
||||||
|
|
||||||
class Messaging (Fabriq e) e (AnyMessage (Encoded e) e) => PeerMessaging e
|
class Messaging (Fabriq e) e (AnyMessage (Encoded e) e) => PeerMessaging e
|
||||||
|
|
||||||
|
@ -125,6 +129,7 @@ makeResponse h = AnyProtocol { myProtoId = natVal (Proxy @(ProtocolId p))
|
||||||
data PeerEnv e =
|
data PeerEnv e =
|
||||||
PeerEnv
|
PeerEnv
|
||||||
{ _envSelf :: Peer e
|
{ _envSelf :: Peer e
|
||||||
|
, _envPeerNonce :: PeerNonce
|
||||||
, _envFab :: Fabriq e
|
, _envFab :: Fabriq e
|
||||||
, _envStorage :: AnyStorage
|
, _envStorage :: AnyStorage
|
||||||
, _envPeerLocator :: AnyPeerLocator e
|
, _envPeerLocator :: AnyPeerLocator e
|
||||||
|
@ -182,9 +187,8 @@ instance Monad m => HasFabriq e (PeerM e m) where
|
||||||
instance Monad m => HasStorage (PeerM e m) where
|
instance Monad m => HasStorage (PeerM e m) where
|
||||||
getStorage = asks (view envStorage)
|
getStorage = asks (view envStorage)
|
||||||
|
|
||||||
-- instance Monad m => HasKeys 'Sign e (PeerM e m) where
|
instance Monad m => HasPeerNonce e (PeerM e m) where
|
||||||
-- getPrivateKey = asks (view (envCred . peerSignSk))
|
peerNonce = asks (view envPeerNonce)
|
||||||
-- getPublicKey = asks (view (envCred . peerSignPk))
|
|
||||||
|
|
||||||
instance ( MonadIO m
|
instance ( MonadIO m
|
||||||
-- , HasProtocol e p
|
-- , HasProtocol e p
|
||||||
|
@ -289,8 +293,7 @@ sweep = do
|
||||||
|
|
||||||
liftIO $ atomically $ modifyTVar' sw (<> HashMap.fromList (mconcat alive))
|
liftIO $ atomically $ modifyTVar' sw (<> HashMap.fromList (mconcat alive))
|
||||||
|
|
||||||
instance ( HasProtocol e p
|
instance ( Typeable (EventKey e p)
|
||||||
, Typeable (EventKey e p)
|
|
||||||
, Typeable (Event e p)
|
, Typeable (Event e p)
|
||||||
, Hashable (EventKey e p)
|
, Hashable (EventKey e p)
|
||||||
, Eq (EventKey e p)
|
, Eq (EventKey e p)
|
||||||
|
@ -320,22 +323,41 @@ instance ( HasProtocol e p
|
||||||
|
|
||||||
void $ liftIO $ atomically $ modifyTVar' se (HashMap.insert sk (mconcat pers))
|
void $ liftIO $ atomically $ modifyTVar' se (HashMap.insert sk (mconcat pers))
|
||||||
|
|
||||||
runPeerM :: forall e m . (MonadIO m, HasPeer e, Ord (Peer e), Pretty (Peer e))
|
|
||||||
=> AnyStorage
|
|
||||||
-> Fabriq e
|
|
||||||
-> Peer e
|
|
||||||
-> PeerM e m ()
|
|
||||||
-> m ()
|
|
||||||
|
|
||||||
runPeerM s bus p f = do
|
newPeerEnv :: forall e m . ( MonadIO m
|
||||||
|
, HasPeer e
|
||||||
|
, Ord (Peer e)
|
||||||
|
, Pretty (Peer e)
|
||||||
|
, HasNonces () m
|
||||||
|
)
|
||||||
|
=> AnyStorage
|
||||||
|
-> Fabriq e
|
||||||
|
-> Peer e
|
||||||
|
-> m (PeerEnv e)
|
||||||
|
|
||||||
|
newPeerEnv s bus p = do
|
||||||
|
|
||||||
pl <- AnyPeerLocator <$> newStaticPeerLocator @e mempty
|
pl <- AnyPeerLocator <$> newStaticPeerLocator @e mempty
|
||||||
|
|
||||||
env <- PeerEnv p bus s pl <$> newPipeline defProtoPipelineSize
|
nonce <- newNonce @()
|
||||||
<*> liftIO (Cache.newCache (Just defCookieTimeout))
|
|
||||||
<*> liftIO (newTVarIO mempty)
|
PeerEnv p nonce bus s pl <$> newPipeline defProtoPipelineSize
|
||||||
<*> liftIO (Cache.newCache (Just defCookieTimeout))
|
<*> liftIO (Cache.newCache (Just defCookieTimeout))
|
||||||
<*> liftIO (newTVarIO mempty)
|
<*> liftIO (newTVarIO mempty)
|
||||||
|
<*> liftIO (Cache.newCache (Just defCookieTimeout))
|
||||||
|
<*> liftIO (newTVarIO mempty)
|
||||||
|
|
||||||
|
runPeerM :: forall e m . ( MonadIO m
|
||||||
|
, HasPeer e
|
||||||
|
, Ord (Peer e)
|
||||||
|
, Pretty (Peer e)
|
||||||
|
, HasNonces () m
|
||||||
|
)
|
||||||
|
=> PeerEnv e
|
||||||
|
-> PeerM e m ()
|
||||||
|
-> m ()
|
||||||
|
|
||||||
|
runPeerM env f = do
|
||||||
|
|
||||||
let de = view envDeferred env
|
let de = view envDeferred env
|
||||||
as <- liftIO $ replicateM 8 $ async $ runPipeline de
|
as <- liftIO $ replicateM 8 $ async $ runPipeline de
|
||||||
|
@ -440,3 +462,8 @@ instance ( MonadIO m
|
||||||
instance (Monad m, HasOwnPeer e m) => HasOwnPeer e (ResponseM e m) where
|
instance (Monad m, HasOwnPeer e m) => HasOwnPeer e (ResponseM e m) where
|
||||||
ownPeer = lift ownPeer
|
ownPeer = lift ownPeer
|
||||||
|
|
||||||
|
|
||||||
|
instance (Monad m, HasFabriq e m) => HasFabriq e (ResponseM e m) where
|
||||||
|
getFabriq = lift getFabriq
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,11 @@ defMessageQueueSize :: Integral a => a
|
||||||
defMessageQueueSize = 65536
|
defMessageQueueSize = 65536
|
||||||
|
|
||||||
defBurst :: Integral a => a
|
defBurst :: Integral a => a
|
||||||
defBurst = 64
|
defBurst = 16
|
||||||
|
|
||||||
-- defChunkSize :: Integer
|
-- defChunkSize :: Integer
|
||||||
defChunkSize :: Integral a => a
|
defChunkSize :: Integral a => a
|
||||||
defChunkSize = 1024
|
defChunkSize = 1200
|
||||||
|
|
||||||
defBlockSize :: Integer
|
defBlockSize :: Integer
|
||||||
defBlockSize = 256 * 1024
|
defBlockSize = 256 * 1024
|
||||||
|
@ -42,7 +42,7 @@ defProtoPipelineSize :: Int
|
||||||
defProtoPipelineSize = 65536*4
|
defProtoPipelineSize = 65536*4
|
||||||
|
|
||||||
defCookieTimeoutSec :: Timeout 'Seconds
|
defCookieTimeoutSec :: Timeout 'Seconds
|
||||||
defCookieTimeoutSec = 120
|
defCookieTimeoutSec = 1200
|
||||||
|
|
||||||
defCookieTimeout :: TimeSpec
|
defCookieTimeout :: TimeSpec
|
||||||
defCookieTimeout = toTimeSpec defCookieTimeoutSec
|
defCookieTimeout = toTimeSpec defCookieTimeoutSec
|
||||||
|
@ -52,14 +52,17 @@ defBlockInfoTimeout = 2
|
||||||
|
|
||||||
-- how much time wait for block from peer?
|
-- how much time wait for block from peer?
|
||||||
defBlockWaitMax :: Timeout 'Seconds
|
defBlockWaitMax :: Timeout 'Seconds
|
||||||
defBlockWaitMax = 3 :: Timeout 'Seconds
|
defBlockWaitMax = 5.0 :: Timeout 'Seconds
|
||||||
|
|
||||||
-- how much time wait for block from peer?
|
-- how much time wait for block from peer?
|
||||||
defChunkWaitMax :: Timeout 'Seconds
|
defChunkWaitMax :: Timeout 'Seconds
|
||||||
defChunkWaitMax = 1 :: Timeout 'Seconds
|
defChunkWaitMax = 1.0 :: Timeout 'Seconds
|
||||||
|
|
||||||
defSweepTimeout :: Timeout 'Seconds
|
defSweepTimeout :: Timeout 'Seconds
|
||||||
defSweepTimeout = 30 -- FIXME: only for debug!
|
defSweepTimeout = 30 -- FIXME: only for debug!
|
||||||
|
|
||||||
|
-- FIXME: debug only!
|
||||||
|
defPeerAnnounceTime :: Timeout 'Seconds
|
||||||
|
defPeerAnnounceTime = 30
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,10 @@ data family Event e a :: Type
|
||||||
|
|
||||||
type EventHandler e a m = Event e a -> m ()
|
type EventHandler e a m = Event e a -> m ()
|
||||||
|
|
||||||
class Monad m => EventListener e a m | a -> e where
|
class Monad m => EventListener e a m where
|
||||||
subscribe :: EventKey e a -> EventHandler e a m -> m ()
|
subscribe :: EventKey e a -> EventHandler e a m -> m ()
|
||||||
|
|
||||||
class Monad m => EventEmitter e a m | a -> e where
|
class Monad m => EventEmitter e a m where
|
||||||
emit :: EventKey e a -> Event e a -> m ()
|
emit :: EventKey e a -> Event e a -> m ()
|
||||||
|
|
||||||
class EventType a where
|
class EventType a where
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{-# Language TemplateHaskell #-}
|
{-# Language TemplateHaskell #-}
|
||||||
|
{-# Language UndecidableInstances #-}
|
||||||
module HBS2.Net.Proto.BlockAnnounce where
|
module HBS2.Net.Proto.BlockAnnounce where
|
||||||
|
|
||||||
import HBS2.Prelude.Plated
|
import HBS2.Prelude.Plated
|
||||||
|
@ -38,10 +39,10 @@ instance Serialise BlockInfoNonce
|
||||||
instance Serialise (BlockAnnounceInfo e)
|
instance Serialise (BlockAnnounceInfo e)
|
||||||
|
|
||||||
|
|
||||||
newtype BlockAnnounce e = BlockAnnounce (BlockAnnounceInfo e)
|
data BlockAnnounce e = BlockAnnounce PeerNonce (BlockAnnounceInfo e)
|
||||||
deriving stock (Eq,Generic,Show)
|
deriving stock (Generic)
|
||||||
|
|
||||||
instance Serialise (BlockAnnounce e)
|
instance Serialise PeerNonce => Serialise (BlockAnnounce e)
|
||||||
|
|
||||||
|
|
||||||
makeLenses ''BlockAnnounceInfo
|
makeLenses ''BlockAnnounceInfo
|
||||||
|
@ -53,16 +54,16 @@ blockAnnounceProto :: forall e m . ( MonadIO m
|
||||||
) => BlockAnnounce e -> m ()
|
) => BlockAnnounce e -> m ()
|
||||||
blockAnnounceProto =
|
blockAnnounceProto =
|
||||||
\case
|
\case
|
||||||
BlockAnnounce info -> do
|
BlockAnnounce n info -> do
|
||||||
that <- thatPeer (Proxy @(BlockAnnounce e))
|
that <- thatPeer (Proxy @(BlockAnnounce e))
|
||||||
emit @e BlockAnnounceInfoKey (BlockAnnounceEvent that info)
|
emit @e BlockAnnounceInfoKey (BlockAnnounceEvent that info n)
|
||||||
|
|
||||||
data instance EventKey e (BlockAnnounce e) =
|
data instance EventKey e (BlockAnnounce e) =
|
||||||
BlockAnnounceInfoKey
|
BlockAnnounceInfoKey
|
||||||
deriving stock (Typeable, Eq,Generic)
|
deriving stock (Typeable, Eq,Generic)
|
||||||
|
|
||||||
data instance Event e (BlockAnnounce e) =
|
data instance Event e (BlockAnnounce e) =
|
||||||
BlockAnnounceEvent (Peer e) (BlockAnnounceInfo e)
|
BlockAnnounceEvent (Peer e) (BlockAnnounceInfo e) PeerNonce
|
||||||
deriving stock (Typeable)
|
deriving stock (Typeable)
|
||||||
|
|
||||||
instance Typeable (BlockAnnounceInfo e) => Hashable (EventKey e (BlockAnnounce e)) where
|
instance Typeable (BlockAnnounceInfo e) => Hashable (EventKey e (BlockAnnounce e)) where
|
||||||
|
@ -74,4 +75,3 @@ instance EventType ( Event e ( BlockAnnounce e) ) where
|
||||||
isPersistent = True
|
isPersistent = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import HBS2.Net.Proto.BlockAnnounce
|
||||||
import HBS2.Net.Proto.BlockChunks
|
import HBS2.Net.Proto.BlockChunks
|
||||||
import HBS2.Net.Proto.BlockInfo
|
import HBS2.Net.Proto.BlockInfo
|
||||||
import HBS2.Net.Proto.Peer
|
import HBS2.Net.Proto.Peer
|
||||||
|
import HBS2.Net.Proto.PeerAnnounce
|
||||||
import HBS2.Defaults
|
import HBS2.Defaults
|
||||||
|
|
||||||
import Data.Functor
|
import Data.Functor
|
||||||
|
@ -25,6 +26,7 @@ import Crypto.Saltine.Core.Box qualified as Crypto
|
||||||
import Crypto.Saltine.Class qualified as Crypto
|
import Crypto.Saltine.Class qualified as Crypto
|
||||||
import Crypto.Saltine.Core.Sign qualified as Sign
|
import Crypto.Saltine.Core.Sign qualified as Sign
|
||||||
|
|
||||||
|
|
||||||
type instance PubKey 'Sign e = Sign.PublicKey
|
type instance PubKey 'Sign e = Sign.PublicKey
|
||||||
type instance PrivKey 'Sign e = Sign.SecretKey
|
type instance PrivKey 'Sign e = Sign.SecretKey
|
||||||
|
|
||||||
|
@ -57,6 +59,13 @@ instance HasProtocol UDP (PeerHandshake UDP) where
|
||||||
decode = either (const Nothing) Just . deserialiseOrFail
|
decode = either (const Nothing) Just . deserialiseOrFail
|
||||||
encode = serialise
|
encode = serialise
|
||||||
|
|
||||||
|
instance HasProtocol UDP (PeerAnnounce UDP) where
|
||||||
|
type instance ProtocolId (PeerAnnounce UDP) = 5
|
||||||
|
type instance Encoded UDP = ByteString
|
||||||
|
decode = either (const Nothing) Just . deserialiseOrFail
|
||||||
|
encode = serialise
|
||||||
|
|
||||||
|
|
||||||
instance Expires (SessionKey UDP (BlockInfo UDP)) where
|
instance Expires (SessionKey UDP (BlockInfo UDP)) where
|
||||||
expiresIn _ = Just defCookieTimeoutSec
|
expiresIn _ = Just defCookieTimeoutSec
|
||||||
|
|
||||||
|
@ -75,12 +84,21 @@ instance Expires (SessionKey UDP (KnownPeer UDP)) where
|
||||||
instance Expires (SessionKey UDP (PeerHandshake UDP)) where
|
instance Expires (SessionKey UDP (PeerHandshake UDP)) where
|
||||||
expiresIn _ = Just 10
|
expiresIn _ = Just 10
|
||||||
|
|
||||||
|
instance Expires (EventKey UDP (PeerAnnounce UDP)) where
|
||||||
|
expiresIn _ = Nothing
|
||||||
|
|
||||||
|
|
||||||
instance MonadIO m => HasNonces (PeerHandshake UDP) m where
|
instance MonadIO m => HasNonces (PeerHandshake UDP) m where
|
||||||
type instance Nonce (PeerHandshake UDP) = BS.ByteString
|
type instance Nonce (PeerHandshake UDP) = BS.ByteString
|
||||||
newNonce = do
|
newNonce = do
|
||||||
n <- liftIO ( Crypto.newNonce <&> Crypto.encode )
|
n <- liftIO ( Crypto.newNonce <&> Crypto.encode )
|
||||||
pure $ BS.take 32 n
|
pure $ BS.take 32 n
|
||||||
|
|
||||||
|
instance MonadIO m => HasNonces () m where
|
||||||
|
type instance Nonce () = BS.ByteString
|
||||||
|
newNonce = do
|
||||||
|
n <- liftIO ( Crypto.newNonce <&> Crypto.encode )
|
||||||
|
pure $ BS.take 32 n
|
||||||
|
|
||||||
instance Serialise Sign.Signature
|
instance Serialise Sign.Signature
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,13 @@ module HBS2.Net.Proto.Peer where
|
||||||
import HBS2.Base58
|
import HBS2.Base58
|
||||||
import HBS2.Data.Types
|
import HBS2.Data.Types
|
||||||
import HBS2.Events
|
import HBS2.Events
|
||||||
import HBS2.Net.Auth.Credentials
|
|
||||||
import HBS2.Net.PeerLocator
|
|
||||||
import HBS2.Net.Proto
|
import HBS2.Net.Proto
|
||||||
import HBS2.Clock
|
import HBS2.Clock
|
||||||
import HBS2.Net.Proto.Sessions
|
import HBS2.Net.Proto.Sessions
|
||||||
import HBS2.Prelude.Plated
|
import HBS2.Prelude.Plated
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
import Codec.Serialise()
|
import Codec.Serialise()
|
||||||
import Data.ByteString.Lazy (ByteString)
|
|
||||||
import Data.ByteString qualified as BS
|
import Data.ByteString qualified as BS
|
||||||
import Data.Hashable
|
import Data.Hashable
|
||||||
import Lens.Micro.Platform
|
import Lens.Micro.Platform
|
||||||
|
@ -32,9 +30,6 @@ newtype PeerData e =
|
||||||
|
|
||||||
makeLenses 'PeerData
|
makeLenses 'PeerData
|
||||||
|
|
||||||
newtype PeerAnnounce e = PeerAnnounce (PeerData e)
|
|
||||||
deriving stock (Generic)
|
|
||||||
|
|
||||||
data PeerHandshake e =
|
data PeerHandshake e =
|
||||||
PeerPing PingNonce
|
PeerPing PingNonce
|
||||||
| PeerPong (Signature e) (PeerData e)
|
| PeerPong (Signature e) (PeerData e)
|
||||||
|
@ -74,6 +69,7 @@ sendPing pip = do
|
||||||
|
|
||||||
peerHandShakeProto :: forall e m . ( MonadIO m
|
peerHandShakeProto :: forall e m . ( MonadIO m
|
||||||
, Response e (PeerHandshake e) m
|
, Response e (PeerHandshake e) m
|
||||||
|
, Request e (PeerHandshake e) m
|
||||||
, Sessions e (PeerHandshake e) m
|
, Sessions e (PeerHandshake e) m
|
||||||
, Sessions e (KnownPeer e) m
|
, Sessions e (KnownPeer e) m
|
||||||
, HasNonces (PeerHandshake e) m
|
, HasNonces (PeerHandshake e) m
|
||||||
|
@ -99,6 +95,13 @@ peerHandShakeProto =
|
||||||
-- TODO: отправить обратно вместе с публичным ключом
|
-- TODO: отправить обратно вместе с публичным ключом
|
||||||
response (PeerPong @e sign (PeerData (view peerSignPk creds)))
|
response (PeerPong @e sign (PeerData (view peerSignPk creds)))
|
||||||
|
|
||||||
|
-- TODO: да и пингануть того самим
|
||||||
|
|
||||||
|
se <- find (KnownPeerKey pip) id <&> isJust
|
||||||
|
|
||||||
|
unless se $ do
|
||||||
|
sendPing pip
|
||||||
|
|
||||||
PeerPong sign d -> do
|
PeerPong sign d -> do
|
||||||
pip <- thatPeer proto
|
pip <- thatPeer proto
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
{-# Language UndecidableInstances #-}
|
||||||
|
module HBS2.Net.Proto.PeerAnnounce where
|
||||||
|
|
||||||
|
import HBS2.Prelude.Plated
|
||||||
|
import HBS2.Net.Proto
|
||||||
|
import HBS2.Events
|
||||||
|
|
||||||
|
import Type.Reflection (someTypeRep)
|
||||||
|
import Data.Hashable
|
||||||
|
import Codec.Serialise (Serialise)
|
||||||
|
|
||||||
|
-- This subprotocol is assumed to work with a
|
||||||
|
-- multicast address for local peer discovery.
|
||||||
|
--
|
||||||
|
-- For single cast case seems that PeerHandshake
|
||||||
|
-- subprotocol is sufficient:
|
||||||
|
-- peer Bob pings peer Alice,
|
||||||
|
-- now both of them know each other.
|
||||||
|
--
|
||||||
|
-- For making life easier in a local network,
|
||||||
|
-- we introduce PeerAnnounce subprotocol.
|
||||||
|
--
|
||||||
|
-- The idea is following:
|
||||||
|
-- Peer sends PeerAnnounce to a multicast address,
|
||||||
|
-- all available peers send their pings and now
|
||||||
|
-- they all know this peer.
|
||||||
|
--
|
||||||
|
|
||||||
|
newtype PeerAnnounce e =
|
||||||
|
PeerAnnounce PeerNonce
|
||||||
|
deriving stock (Typeable, Generic)
|
||||||
|
|
||||||
|
|
||||||
|
peerAnnounceProto :: forall e m . ( MonadIO m
|
||||||
|
, EventEmitter e (PeerAnnounce e) m
|
||||||
|
, Response e (PeerAnnounce e) m
|
||||||
|
) => PeerAnnounce e -> m ()
|
||||||
|
peerAnnounceProto =
|
||||||
|
\case
|
||||||
|
PeerAnnounce nonce -> do
|
||||||
|
who <- thatPeer (Proxy @(PeerAnnounce e))
|
||||||
|
emit @e PeerAnnounceEventKey (PeerAnnounceEvent who nonce)
|
||||||
|
|
||||||
|
|
||||||
|
data instance EventKey e (PeerAnnounce e) =
|
||||||
|
PeerAnnounceEventKey
|
||||||
|
deriving stock (Typeable, Eq,Generic)
|
||||||
|
|
||||||
|
data instance Event e (PeerAnnounce e) =
|
||||||
|
PeerAnnounceEvent (Peer e) PeerNonce
|
||||||
|
deriving stock (Typeable)
|
||||||
|
|
||||||
|
instance Typeable (PeerAnnounce e) => Hashable (EventKey e (PeerAnnounce e)) where
|
||||||
|
hashWithSalt salt _ = hashWithSalt salt (someTypeRep p)
|
||||||
|
where
|
||||||
|
p = Proxy @(PeerAnnounce e)
|
||||||
|
|
||||||
|
instance EventType ( Event e ( PeerAnnounce e) ) where
|
||||||
|
isPersistent = True
|
||||||
|
|
||||||
|
|
||||||
|
instance Serialise PeerNonce => Serialise (PeerAnnounce e)
|
||||||
|
|
|
@ -47,6 +47,7 @@ class HasCookie e p | p -> e where
|
||||||
getCookie :: p -> Maybe (Cookie e)
|
getCookie :: p -> Maybe (Cookie e)
|
||||||
getCookie = const Nothing
|
getCookie = const Nothing
|
||||||
|
|
||||||
|
type PeerNonce = Nonce ()
|
||||||
|
|
||||||
data PeerCredentials e =
|
data PeerCredentials e =
|
||||||
PeerCredentials
|
PeerCredentials
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
{-# Language UndecidableInstances #-}
|
||||||
|
module HBS2.System.Logger.Simple
|
||||||
|
( withSimpleLogger
|
||||||
|
, debug
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Data.Foldable
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import System.Log.FastLogger
|
||||||
|
import System.Log.FastLogger.LoggerSet
|
||||||
|
import Data.IORef
|
||||||
|
import System.IO.Unsafe
|
||||||
|
import Prettyprinter
|
||||||
|
|
||||||
|
loggers :: IORef (Maybe LoggerSet)
|
||||||
|
loggers = unsafePerformIO (newIORef Nothing)
|
||||||
|
{-# NOINLINE loggers #-}
|
||||||
|
|
||||||
|
|
||||||
|
withSimpleLogger :: IO () -> IO ()
|
||||||
|
withSimpleLogger program = do
|
||||||
|
set <- newStdoutLoggerSet 10000
|
||||||
|
void $ atomicModifyIORef' loggers $ \case
|
||||||
|
Nothing -> (Just set, Just set)
|
||||||
|
Just s -> (Just s, Just s)
|
||||||
|
program
|
||||||
|
withLogger flushLogStr
|
||||||
|
|
||||||
|
withLogger :: MonadIO m => (LoggerSet -> m b) -> m ()
|
||||||
|
withLogger f = do
|
||||||
|
lo <- liftIO $ readIORef loggers
|
||||||
|
forM_ lo f
|
||||||
|
|
||||||
|
debug :: (MonadIO m, ToLogStr a) => a -> m ()
|
||||||
|
debug s = do
|
||||||
|
liftIO $ withLogger $ \set -> pushLogStrLn set (toLogStr s)
|
||||||
|
|
||||||
|
|
||||||
|
instance {-# OVERLAPPABLE #-} Pretty a => ToLogStr a where
|
||||||
|
toLogStr p = toLogStr (show (pretty p))
|
||||||
|
|
||||||
|
|
||||||
|
instance {-# OVERLAPPABLE #-} ToLogStr (Doc ann) where
|
||||||
|
toLogStr p = toLogStr (show p)
|
||||||
|
|
|
@ -17,9 +17,9 @@ import HBS2.Net.Proto.Definition
|
||||||
import HBS2.Net.Proto.Sessions
|
import HBS2.Net.Proto.Sessions
|
||||||
import HBS2.Prelude.Plated
|
import HBS2.Prelude.Plated
|
||||||
import HBS2.Storage
|
import HBS2.Storage
|
||||||
|
import HBS2.System.Logger.Simple
|
||||||
|
|
||||||
import PeerInfo
|
import PeerInfo
|
||||||
import Logger
|
|
||||||
|
|
||||||
import Data.Foldable hiding (find)
|
import Data.Foldable hiding (find)
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
|
@ -344,8 +344,8 @@ blockDownloadLoop :: forall e m . ( m ~ PeerM e IO
|
||||||
, Block ByteString ~ ByteString
|
, Block ByteString ~ ByteString
|
||||||
, PeerMessaging e
|
, PeerMessaging e
|
||||||
)
|
)
|
||||||
=> m ()
|
=> DownloadEnv e -> m ()
|
||||||
blockDownloadLoop = do
|
blockDownloadLoop env0 = do
|
||||||
|
|
||||||
e <- ask
|
e <- ask
|
||||||
stor <- getStorage
|
stor <- getStorage
|
||||||
|
@ -361,19 +361,21 @@ blockDownloadLoop = do
|
||||||
|
|
||||||
npi <- newPeerInfo
|
npi <- newPeerInfo
|
||||||
|
|
||||||
|
debug $ "known peers" <+> pretty pee
|
||||||
|
|
||||||
for_ pee $ \p -> do
|
for_ pee $ \p -> do
|
||||||
pinfo <- fetch True npi (PeerInfoKey p) id
|
pinfo <- fetch True npi (PeerInfoKey p) id
|
||||||
burst <- liftIO $ readTVarIO (view peerBurst pinfo)
|
burst <- liftIO $ readTVarIO (view peerBurst pinfo)
|
||||||
debug $ "peer" <+> pretty p <+> "burst: " <+> pretty burst
|
debug $ "peer" <+> pretty p <+> "burst: " <+> pretty burst
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
runDownloadM @e $ do
|
withDownload env0 do
|
||||||
|
|
||||||
env <- ask
|
env <- ask
|
||||||
|
|
||||||
let again h = do
|
let again h = do
|
||||||
debug $ "block fucked: " <+> pretty h
|
debug $ "block fucked: " <+> pretty h
|
||||||
withPeerM e $ withDownload env (addDownload h)
|
withPeerM e $ withDownload env (processBlock h)
|
||||||
|
|
||||||
mapM_ processBlock blks
|
mapM_ processBlock blks
|
||||||
|
|
||||||
|
@ -390,7 +392,7 @@ blockDownloadLoop = do
|
||||||
|
|
||||||
liftIO $ race ( pause defBlockWaitMax >> again h ) do
|
liftIO $ race ( pause defBlockWaitMax >> again h ) do
|
||||||
withPeerM e $ withDownload env $ do -- NOTE: really crazy shit
|
withPeerM e $ withDownload env $ do -- NOTE: really crazy shit
|
||||||
withFreePeer p (addDownload h >> pause (0.1 :: Timeout 'Seconds)) do
|
withFreePeer p (processBlock h >> pause (0.1 :: Timeout 'Seconds)) do
|
||||||
downloadFromWithPeer p h
|
downloadFromWithPeer p h
|
||||||
|
|
||||||
next
|
next
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
module Logger where
|
|
||||||
|
|
||||||
import HBS2.Prelude
|
|
||||||
|
|
||||||
import System.IO
|
|
||||||
import Prettyprinter
|
|
||||||
|
|
||||||
debug :: (MonadIO m) => Doc ann -> m ()
|
|
||||||
debug p = liftIO $ hPrint stderr p
|
|
||||||
|
|
||||||
|
|
|
@ -17,14 +17,18 @@ import HBS2.Net.PeerLocator
|
||||||
import HBS2.Net.Proto
|
import HBS2.Net.Proto
|
||||||
import HBS2.Net.Proto.Definition
|
import HBS2.Net.Proto.Definition
|
||||||
import HBS2.Net.Proto.Peer
|
import HBS2.Net.Proto.Peer
|
||||||
|
import HBS2.Net.Proto.PeerAnnounce
|
||||||
import HBS2.Net.Proto.Sessions
|
import HBS2.Net.Proto.Sessions
|
||||||
import HBS2.OrDie
|
import HBS2.OrDie
|
||||||
import HBS2.Prelude.Plated
|
import HBS2.Prelude.Plated
|
||||||
import HBS2.Storage.Simple
|
import HBS2.Storage.Simple
|
||||||
|
import HBS2.System.Logger.Simple
|
||||||
|
|
||||||
import RPC
|
import RPC
|
||||||
import BlockDownload
|
import BlockDownload
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
|
import Crypto.Saltine (sodiumInit)
|
||||||
import Data.Function
|
import Data.Function
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
|
@ -42,9 +46,6 @@ import System.Directory
|
||||||
import System.Exit
|
import System.Exit
|
||||||
import System.IO
|
import System.IO
|
||||||
|
|
||||||
debug :: (MonadIO m) => Doc ann -> m ()
|
|
||||||
debug p = liftIO $ hPrint stderr p
|
|
||||||
|
|
||||||
defStorageThreads :: Integral a => a
|
defStorageThreads :: Integral a => a
|
||||||
defStorageThreads = 4
|
defStorageThreads = 4
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ data RPCCommand =
|
||||||
POKE
|
POKE
|
||||||
| ANNOUNCE (Hash HbSync)
|
| ANNOUNCE (Hash HbSync)
|
||||||
| PING (PeerAddr UDP)
|
| PING (PeerAddr UDP)
|
||||||
|
| CHECK PeerNonce (PeerAddr UDP) (Hash HbSync)
|
||||||
|
|
||||||
data PeerOpts =
|
data PeerOpts =
|
||||||
PeerOpts
|
PeerOpts
|
||||||
|
@ -186,9 +188,18 @@ instance ( Monad m
|
||||||
|
|
||||||
response = lift . response
|
response = lift . response
|
||||||
|
|
||||||
runPeer :: () => PeerOpts -> IO ()
|
|
||||||
runPeer opts = Exception.handle myException $ do
|
|
||||||
|
|
||||||
|
-- FIXME: Нормальные синхронизированные логи. Можно даже цветные.
|
||||||
|
-- Ориентированные на Prettyprinter.
|
||||||
|
-- Без лишнего мусора.
|
||||||
|
|
||||||
|
-- FIXME: Убрать хардкод UDP отовсюду ниже.
|
||||||
|
-- Вынести в сигнатуру.
|
||||||
|
|
||||||
|
runPeer :: PeerOpts -> IO ()
|
||||||
|
runPeer opts = Exception.handle myException $ withSimpleLogger do
|
||||||
|
|
||||||
|
sodiumInit
|
||||||
|
|
||||||
rpcQ <- newTQueueIO @RPCCommand
|
rpcQ <- newTQueueIO @RPCCommand
|
||||||
|
|
||||||
|
@ -217,6 +228,8 @@ runPeer opts = Exception.handle myException $ do
|
||||||
|
|
||||||
`orDie` "assertion: localMulticastPeer not set"
|
`orDie` "assertion: localMulticastPeer not set"
|
||||||
|
|
||||||
|
debug $ pretty localMulticast
|
||||||
|
|
||||||
mess <- newMessagingUDP False (Just (view listenOn opts))
|
mess <- newMessagingUDP False (Just (view listenOn opts))
|
||||||
`orDie` "unable listen on the given addr"
|
`orDie` "unable listen on the given addr"
|
||||||
|
|
||||||
|
@ -235,22 +248,39 @@ runPeer opts = Exception.handle myException $ do
|
||||||
messMcast <- async $ runMessagingUDP mcast
|
messMcast <- async $ runMessagingUDP mcast
|
||||||
`catch` (\(e::SomeException) -> throwIO e )
|
`catch` (\(e::SomeException) -> throwIO e )
|
||||||
|
|
||||||
|
denv <- newDownloadEnv
|
||||||
|
|
||||||
|
penv <- newPeerEnv (AnyStorage s) (Fabriq mess) (getOwnPeer mess)
|
||||||
|
|
||||||
loop <- async do
|
loop <- async do
|
||||||
|
|
||||||
runPeerM (AnyStorage s) (Fabriq mess) (getOwnPeer mess) $ do
|
runPeerM penv $ do
|
||||||
adapter <- mkAdapter
|
adapter <- mkAdapter
|
||||||
env <- ask
|
env <- ask
|
||||||
|
|
||||||
|
pnonce <- peerNonce @UDP
|
||||||
|
|
||||||
pl <- getPeerLocator @UDP
|
pl <- getPeerLocator @UDP
|
||||||
|
|
||||||
addPeers @UDP pl ps
|
addPeers @UDP pl ps
|
||||||
|
|
||||||
|
subscribe @UDP PeerAnnounceEventKey $ \pe@(PeerAnnounceEvent pip nonce) -> do
|
||||||
|
unless (nonce == pnonce) $ do
|
||||||
|
debug $ "Got peer announce!" <+> pretty pip
|
||||||
|
known <- find (KnownPeerKey pip) id <&> isJust
|
||||||
|
unless known $ sendPing pip
|
||||||
|
|
||||||
subscribe @UDP KnownPeerEventKey $ \(KnownPeerEvent p d) -> do
|
subscribe @UDP KnownPeerEventKey $ \(KnownPeerEvent p d) -> do
|
||||||
addPeers pl [p]
|
addPeers pl [p]
|
||||||
debug $ "Got authorized peer!" <+> pretty p
|
debug $ "Got authorized peer!" <+> pretty p
|
||||||
<+> pretty (AsBase58 (view peerSignKey d))
|
<+> pretty (AsBase58 (view peerSignKey d))
|
||||||
|
|
||||||
as <- liftIO $ async $ withPeerM env blockDownloadLoop
|
void $ liftIO $ async $ withPeerM env $ forever $ do
|
||||||
|
pause defPeerAnnounceTime -- FIXME: setting!
|
||||||
|
debug "sending local peer announce"
|
||||||
|
request localMulticast (PeerAnnounce @UDP pnonce)
|
||||||
|
|
||||||
|
as <- liftIO $ async $ withPeerM env (blockDownloadLoop denv)
|
||||||
|
|
||||||
rpc <- liftIO $ async $ withPeerM env $ forever $ do
|
rpc <- liftIO $ async $ withPeerM env $ forever $ do
|
||||||
cmd <- liftIO $ atomically $ readTQueue rpcQ
|
cmd <- liftIO $ atomically $ readTQueue rpcQ
|
||||||
|
@ -269,7 +299,32 @@ runPeer opts = Exception.handle myException $ do
|
||||||
|
|
||||||
maybe1 mbsize (pure ()) $ \size -> do
|
maybe1 mbsize (pure ()) $ \size -> do
|
||||||
let ann = BlockAnnounceInfo 0 NoBlockInfoMeta size h
|
let ann = BlockAnnounceInfo 0 NoBlockInfoMeta size h
|
||||||
request localMulticast (BlockAnnounce @UDP ann)
|
no <- peerNonce @UDP
|
||||||
|
request localMulticast (BlockAnnounce @UDP no ann)
|
||||||
|
|
||||||
|
CHECK nonce pa h -> do
|
||||||
|
pip <- fromPeerAddr @UDP pa
|
||||||
|
|
||||||
|
n1 <- peerNonce @UDP
|
||||||
|
|
||||||
|
unless (nonce == n1) do
|
||||||
|
|
||||||
|
peer <- find @UDP (KnownPeerKey pip) id
|
||||||
|
|
||||||
|
debug $ "received announce from"
|
||||||
|
<+> pretty pip
|
||||||
|
<+> pretty h
|
||||||
|
|
||||||
|
case peer of
|
||||||
|
Nothing -> sendPing @UDP pip
|
||||||
|
Just{} -> do
|
||||||
|
debug "announce from a known peer"
|
||||||
|
debug "preparing to dowload shit"
|
||||||
|
debug "checking policy, blah-blah-blah. tomorrow"
|
||||||
|
|
||||||
|
withDownload denv $ do
|
||||||
|
processBlock h
|
||||||
|
|
||||||
|
|
||||||
me <- liftIO $ async $ withPeerM env $ do
|
me <- liftIO $ async $ withPeerM env $ do
|
||||||
runProto @UDP
|
runProto @UDP
|
||||||
|
@ -300,17 +355,24 @@ runPeer opts = Exception.handle myException $ do
|
||||||
[ makeResponse (rpcHandler arpc)
|
[ makeResponse (rpcHandler arpc)
|
||||||
]
|
]
|
||||||
|
|
||||||
ann <- async $ runPeerM (AnyStorage s) (Fabriq mcast) (getOwnPeer mcast) $ do
|
menv <- newPeerEnv (AnyStorage s) (Fabriq mcast) (getOwnPeer mcast)
|
||||||
|
|
||||||
|
ann <- async $ runPeerM menv $ do
|
||||||
|
|
||||||
self <- ownPeer @UDP
|
self <- ownPeer @UDP
|
||||||
|
|
||||||
subscribe @UDP BlockAnnounceInfoKey $ \(BlockAnnounceEvent p bi) -> do
|
subscribe @UDP BlockAnnounceInfoKey $ \(BlockAnnounceEvent p bi no) -> do
|
||||||
unless (p == self) do
|
unless (p == self) do
|
||||||
debug $ "announce" <+> pretty p
|
pa <- toPeerAddr p
|
||||||
<+> pretty (view biHash bi)
|
liftIO $ atomically $ writeTQueue rpcQ (CHECK no pa (view biHash bi))
|
||||||
|
|
||||||
|
subscribe @UDP PeerAnnounceEventKey $ \pe@(PeerAnnounceEvent pip nonce) -> do
|
||||||
|
-- debug $ "Got peer announce!" <+> pretty pip
|
||||||
|
emitToPeer penv PeerAnnounceEventKey pe
|
||||||
|
|
||||||
runProto @UDP
|
runProto @UDP
|
||||||
[ makeResponse blockAnnounceProto
|
[ makeResponse blockAnnounceProto
|
||||||
|
, makeResponse peerAnnounceProto
|
||||||
]
|
]
|
||||||
|
|
||||||
void $ waitAnyCatchCancel $ w <> [udp,loop,rpc,mrpc,ann,messMcast]
|
void $ waitAnyCatchCancel $ w <> [udp,loop,rpc,mrpc,ann,messMcast]
|
||||||
|
@ -318,8 +380,19 @@ runPeer opts = Exception.handle myException $ do
|
||||||
simpleStorageStop s
|
simpleStorageStop s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
emitToPeer :: ( MonadIO m
|
||||||
|
, EventEmitter e a (PeerM e IO)
|
||||||
|
)
|
||||||
|
=> PeerEnv e
|
||||||
|
-> EventKey e a
|
||||||
|
-> Event e a
|
||||||
|
-> m ()
|
||||||
|
|
||||||
|
emitToPeer env k e = liftIO $ withPeerM env (emit k e)
|
||||||
|
|
||||||
withRPC :: String -> RPC UDP -> IO ()
|
withRPC :: String -> RPC UDP -> IO ()
|
||||||
withRPC saddr cmd = do
|
withRPC saddr cmd = withSimpleLogger do
|
||||||
|
|
||||||
as <- parseAddr (fromString saddr) <&> fmap (PeerUDP . addrAddress)
|
as <- parseAddr (fromString saddr) <&> fmap (PeerUDP . addrAddress)
|
||||||
let rpc' = headMay $ L.sortBy (compare `on` addrPriority) as
|
let rpc' = headMay $ L.sortBy (compare `on` addrPriority) as
|
||||||
|
@ -362,3 +435,5 @@ runRpcCommand saddr = \case
|
||||||
PING s -> withRPC saddr (RPCPing s)
|
PING s -> withRPC saddr (RPCPing s)
|
||||||
ANNOUNCE h -> withRPC saddr (RPCAnnounce @UDP h)
|
ANNOUNCE h -> withRPC saddr (RPCAnnounce @UDP h)
|
||||||
|
|
||||||
|
_ -> pure ()
|
||||||
|
|
||||||
|
|
|
@ -2,25 +2,16 @@
|
||||||
{-# Language UndecidableInstances #-}
|
{-# Language UndecidableInstances #-}
|
||||||
module RPC where
|
module RPC where
|
||||||
|
|
||||||
|
|
||||||
import HBS2.Prelude.Plated
|
import HBS2.Prelude.Plated
|
||||||
import HBS2.Net.Proto
|
import HBS2.Net.Proto
|
||||||
import HBS2.Hash
|
import HBS2.Hash
|
||||||
import HBS2.Net.Messaging
|
|
||||||
import HBS2.Net.Messaging.UDP
|
import HBS2.Net.Messaging.UDP
|
||||||
import HBS2.Actors.Peer
|
import HBS2.Actors.Peer
|
||||||
import HBS2.Defaults
|
|
||||||
|
|
||||||
import Logger
|
|
||||||
|
|
||||||
import Control.Concurrent.Async
|
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Data.ByteString.Lazy (ByteString)
|
import Data.ByteString.Lazy (ByteString)
|
||||||
import Codec.Serialise (serialise, deserialiseOrFail,Serialise)
|
import Codec.Serialise (serialise,deserialiseOrFail)
|
||||||
import Lens.Micro.Platform
|
import Lens.Micro.Platform
|
||||||
import Data.Text (Text)
|
|
||||||
|
|
||||||
import Prettyprinter
|
|
||||||
|
|
||||||
data RPC e =
|
data RPC e =
|
||||||
RPCPoke
|
RPCPoke
|
||||||
|
|
|
@ -38,6 +38,7 @@ common common-deps
|
||||||
, random
|
, random
|
||||||
, random-shuffle
|
, random-shuffle
|
||||||
, safe
|
, safe
|
||||||
|
, saltine ^>=0.2.0.1
|
||||||
, serialise
|
, serialise
|
||||||
, split
|
, split
|
||||||
, stm
|
, stm
|
||||||
|
@ -102,7 +103,6 @@ executable hbs2-peer
|
||||||
|
|
||||||
other-modules: BlockDownload
|
other-modules: BlockDownload
|
||||||
, PeerInfo
|
, PeerInfo
|
||||||
, Logger
|
|
||||||
, RPC
|
, RPC
|
||||||
|
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
|
|
|
@ -121,50 +121,6 @@ test-suite test-cw
|
||||||
main-is: TestChunkWriter.hs
|
main-is: TestChunkWriter.hs
|
||||||
|
|
||||||
|
|
||||||
executable test-peer-run
|
|
||||||
import: shared-properties
|
|
||||||
import: common-deps
|
|
||||||
default-language: Haskell2010
|
|
||||||
|
|
||||||
ghc-options:
|
|
||||||
-- -prof
|
|
||||||
-- -fprof-auto
|
|
||||||
|
|
||||||
other-modules:
|
|
||||||
|
|
||||||
-- other-extensions:
|
|
||||||
|
|
||||||
-- type: exitcode-stdio-1.0
|
|
||||||
hs-source-dirs: test
|
|
||||||
main-is: Peer2Main.hs
|
|
||||||
|
|
||||||
build-depends:
|
|
||||||
base ^>=4.15.1.0, hbs2-core, hbs2-storage-simple
|
|
||||||
, async
|
|
||||||
, bytestring
|
|
||||||
, cache
|
|
||||||
, clock
|
|
||||||
, containers
|
|
||||||
, data-default
|
|
||||||
, directory
|
|
||||||
, filepath
|
|
||||||
, hashable
|
|
||||||
, microlens-platform
|
|
||||||
, mtl
|
|
||||||
, mwc-random
|
|
||||||
, prettyprinter
|
|
||||||
, QuickCheck
|
|
||||||
, random
|
|
||||||
, safe
|
|
||||||
, serialise
|
|
||||||
, stm
|
|
||||||
, streaming
|
|
||||||
, tasty
|
|
||||||
, tasty-hunit
|
|
||||||
, transformers
|
|
||||||
, uniplate
|
|
||||||
, vector
|
|
||||||
|
|
||||||
executable test-udp
|
executable test-udp
|
||||||
import: shared-properties
|
import: shared-properties
|
||||||
import: common-deps
|
import: common-deps
|
||||||
|
@ -214,3 +170,54 @@ executable test-udp
|
||||||
, vector
|
, vector
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
executable test-logger
|
||||||
|
import: shared-properties
|
||||||
|
import: common-deps
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
ghc-options:
|
||||||
|
-- -prof
|
||||||
|
-- -fprof-auto
|
||||||
|
|
||||||
|
other-modules:
|
||||||
|
|
||||||
|
-- other-extensions:
|
||||||
|
|
||||||
|
-- type: exitcode-stdio-1.0
|
||||||
|
hs-source-dirs: test
|
||||||
|
main-is: TestLogger.hs
|
||||||
|
|
||||||
|
build-depends:
|
||||||
|
base ^>=4.15.1.0, hbs2-core, hbs2-storage-simple
|
||||||
|
, async
|
||||||
|
, attoparsec
|
||||||
|
, bytestring
|
||||||
|
, cache
|
||||||
|
, clock
|
||||||
|
, containers
|
||||||
|
, data-default
|
||||||
|
, data-textual
|
||||||
|
, directory
|
||||||
|
, hashable
|
||||||
|
, microlens-platform
|
||||||
|
, mtl
|
||||||
|
, mwc-random
|
||||||
|
, network
|
||||||
|
, network-ip
|
||||||
|
, prettyprinter
|
||||||
|
, QuickCheck
|
||||||
|
, random
|
||||||
|
, safe
|
||||||
|
, serialise
|
||||||
|
, stm
|
||||||
|
, streaming
|
||||||
|
, tasty
|
||||||
|
, tasty-hunit
|
||||||
|
, text
|
||||||
|
, transformers
|
||||||
|
, uniplate
|
||||||
|
, vector
|
||||||
|
, fast-logger
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,699 +0,0 @@
|
||||||
{-# Language TemplateHaskell #-}
|
|
||||||
{-# Language UndecidableInstances #-}
|
|
||||||
{-# Language RankNTypes #-}
|
|
||||||
{-# Language AllowAmbiguousTypes #-}
|
|
||||||
{-# LANGUAGE MultiWayIf #-}
|
|
||||||
module Main where
|
|
||||||
|
|
||||||
import HBS2.Prelude
|
|
||||||
import HBS2.Hash
|
|
||||||
import HBS2.Actors
|
|
||||||
import HBS2.Actors.ChunkWriter
|
|
||||||
import HBS2.Actors.Peer
|
|
||||||
import HBS2.Clock
|
|
||||||
import HBS2.Data.Detect
|
|
||||||
import HBS2.Data.Types
|
|
||||||
import HBS2.Defaults
|
|
||||||
import HBS2.Events
|
|
||||||
import HBS2.Merkle
|
|
||||||
import HBS2.Net.Messaging.UDP
|
|
||||||
import HBS2.Net.PeerLocator
|
|
||||||
import HBS2.Net.Proto
|
|
||||||
import HBS2.Net.Proto.BlockAnnounce
|
|
||||||
import HBS2.Net.Proto.BlockChunks
|
|
||||||
import HBS2.Net.Proto.BlockInfo
|
|
||||||
import HBS2.Net.Proto.Definition
|
|
||||||
import HBS2.Net.Proto.Sessions
|
|
||||||
import HBS2.OrDie
|
|
||||||
import HBS2.Prelude.Plated
|
|
||||||
import HBS2.Storage
|
|
||||||
import HBS2.Storage.Simple
|
|
||||||
|
|
||||||
import Test.Tasty.HUnit
|
|
||||||
|
|
||||||
import System.Random.Shuffle
|
|
||||||
import Codec.Serialise hiding (encode,decode)
|
|
||||||
import Control.Concurrent.Async
|
|
||||||
import Control.Concurrent.STM
|
|
||||||
import Control.Concurrent.STM.TBQueue qualified as Q
|
|
||||||
import Control.Monad.Reader
|
|
||||||
import Control.Monad.Trans.Maybe
|
|
||||||
import Data.ByteString.Lazy (ByteString)
|
|
||||||
import Data.ByteString.Lazy.Char8 qualified as B8
|
|
||||||
import Data.Default
|
|
||||||
import Data.Foldable hiding (find)
|
|
||||||
import Data.Map (Map)
|
|
||||||
import Data.Map qualified as Map
|
|
||||||
import Data.Maybe
|
|
||||||
import Data.Word
|
|
||||||
import Lens.Micro.Platform
|
|
||||||
import Prettyprinter hiding (pipe)
|
|
||||||
import System.Directory
|
|
||||||
import System.Exit
|
|
||||||
import System.FilePath.Posix
|
|
||||||
import System.IO
|
|
||||||
import Data.Hashable
|
|
||||||
import Type.Reflection
|
|
||||||
import Data.Fixed
|
|
||||||
import Data.HashMap.Strict (HashMap)
|
|
||||||
import Data.HashMap.Strict qualified as HashMap
|
|
||||||
import Data.List qualified as List
|
|
||||||
import Data.List.Split (chunksOf)
|
|
||||||
|
|
||||||
import Data.IntMap (IntMap)
|
|
||||||
import Data.IntMap qualified as IntMap
|
|
||||||
import Data.IntSet qualified as IntSet
|
|
||||||
|
|
||||||
import Data.Dynamic
|
|
||||||
|
|
||||||
debug :: (MonadIO m) => Doc ann -> m ()
|
|
||||||
debug p = liftIO $ hPrint stderr p
|
|
||||||
|
|
||||||
|
|
||||||
calcBursts :: forall a . Integral a => a -> [a] -> [(a,a)]
|
|
||||||
calcBursts bu pieces = go seed
|
|
||||||
where
|
|
||||||
seed = fmap (,1) pieces
|
|
||||||
|
|
||||||
go ( (n1,s1) : (n2,s2) : xs )
|
|
||||||
| (s1 + s2) <= bu = go ((n1, s1+s2) : xs)
|
|
||||||
| otherwise = (n1,s1) : go ( (n2,s2) : xs)
|
|
||||||
|
|
||||||
go [x] = [x]
|
|
||||||
go [] = []
|
|
||||||
|
|
||||||
type Fake = UDP
|
|
||||||
|
|
||||||
newtype PeerInfo e =
|
|
||||||
PeerInfo
|
|
||||||
{ _peerBurst :: TVar Int
|
|
||||||
}
|
|
||||||
deriving stock (Generic,Typeable)
|
|
||||||
|
|
||||||
makeLenses 'PeerInfo
|
|
||||||
|
|
||||||
|
|
||||||
newPeerInfo :: MonadIO m => m (PeerInfo e)
|
|
||||||
newPeerInfo = liftIO do
|
|
||||||
PeerInfo <$> newTVarIO defBurst
|
|
||||||
|
|
||||||
|
|
||||||
type instance SessionData e (PeerInfo e) = PeerInfo e
|
|
||||||
|
|
||||||
newtype instance SessionKey e (PeerInfo e) =
|
|
||||||
PeerInfoKey (Peer e)
|
|
||||||
|
|
||||||
deriving newtype instance Hashable (SessionKey Fake (PeerInfo Fake))
|
|
||||||
deriving stock instance Eq (SessionKey Fake (PeerInfo Fake))
|
|
||||||
|
|
||||||
instance Expires (SessionKey Fake (PeerInfo Fake)) where
|
|
||||||
expiresIn = const (Just 600)
|
|
||||||
|
|
||||||
data BlockDownload =
|
|
||||||
BlockDownload
|
|
||||||
{ _sBlockHash :: Hash HbSync
|
|
||||||
, _sBlockSize :: Size
|
|
||||||
, _sBlockChunkSize :: ChunkSize
|
|
||||||
, _sBlockChunks :: TQueue (ChunkNum, ByteString)
|
|
||||||
}
|
|
||||||
deriving stock (Typeable)
|
|
||||||
|
|
||||||
makeLenses 'BlockDownload
|
|
||||||
|
|
||||||
newBlockDownload :: MonadIO m => Hash HbSync -> m BlockDownload
|
|
||||||
newBlockDownload h = do
|
|
||||||
BlockDownload h 0 0 <$> liftIO newTQueueIO
|
|
||||||
|
|
||||||
|
|
||||||
type instance SessionData e (BlockInfo e) = BlockSizeSession e
|
|
||||||
type instance SessionData e (BlockChunks e) = BlockDownload
|
|
||||||
|
|
||||||
newtype instance SessionKey e (BlockChunks e) =
|
|
||||||
DownloadSessionKey (Peer e, Cookie e)
|
|
||||||
deriving stock (Generic,Typeable)
|
|
||||||
|
|
||||||
newtype BlockSizeSession e =
|
|
||||||
BlockSizeSession
|
|
||||||
{ _bsBlockSizes :: Map (Peer e) Size
|
|
||||||
}
|
|
||||||
|
|
||||||
makeLenses 'BlockSizeSession
|
|
||||||
|
|
||||||
instance Ord (Peer e) => Default (BlockSizeSession e) where
|
|
||||||
def = BlockSizeSession mempty
|
|
||||||
|
|
||||||
deriving stock instance Show (BlockSizeSession Fake)
|
|
||||||
|
|
||||||
deriving newtype instance Hashable (SessionKey Fake (BlockChunks Fake))
|
|
||||||
deriving stock instance Eq (SessionKey Fake (BlockChunks Fake))
|
|
||||||
|
|
||||||
runTestPeer :: (Key HbSync ~ Hash HbSync, Storage (SimpleStorage HbSync) HbSync ByteString (ResponseM Fake (PeerM Fake IO)))
|
|
||||||
=> MessagingUDP
|
|
||||||
-> Peer Fake
|
|
||||||
-> (SimpleStorage HbSync -> ChunkWriter HbSync IO -> IO ())
|
|
||||||
-> IO ()
|
|
||||||
|
|
||||||
runTestPeer mess p zu = do
|
|
||||||
|
|
||||||
dir <- liftIO $ canonicalizePath ( ".peers" </> show (pretty (AsFileName p)))
|
|
||||||
let chDir = dir </> "tmp-chunks"
|
|
||||||
liftIO $ createDirectoryIfMissing True dir
|
|
||||||
|
|
||||||
let opts = [ StoragePrefix dir
|
|
||||||
]
|
|
||||||
|
|
||||||
udp <- async $ runMessagingUDP mess
|
|
||||||
stor <- simpleStorageInit opts
|
|
||||||
cww <- newChunkWriterIO stor (Just chDir)
|
|
||||||
|
|
||||||
sw <- liftIO $ replicateM 8 $ async $ simpleStorageWorker stor
|
|
||||||
cw <- liftIO $ replicateM 8 $ async $ runChunkWriter cww
|
|
||||||
|
|
||||||
zu stor cww
|
|
||||||
|
|
||||||
simpleStorageStop stor
|
|
||||||
stopChunkWriter cww
|
|
||||||
|
|
||||||
mapM_ cancel $ sw <> cw <> [udp]
|
|
||||||
|
|
||||||
|
|
||||||
handleBlockInfo :: forall e m . ( MonadIO m
|
|
||||||
, Sessions e (BlockInfo e) m
|
|
||||||
, Default (SessionData e (BlockInfo e))
|
|
||||||
, Ord (Peer e)
|
|
||||||
, Pretty (Peer e)
|
|
||||||
-- , EventEmitter e (BlockSize e) m
|
|
||||||
)
|
|
||||||
|
|
||||||
=> (Peer e, Hash HbSync, Maybe Integer)
|
|
||||||
-> m ()
|
|
||||||
|
|
||||||
handleBlockInfo (p, h, sz') = do
|
|
||||||
maybe1 sz' (pure ()) $ \sz -> do
|
|
||||||
let bsz = fromIntegral sz
|
|
||||||
update @e def (BlockSizeKey h) (over bsBlockSizes (Map.insert p bsz))
|
|
||||||
|
|
||||||
|
|
||||||
data Stats e =
|
|
||||||
Stats
|
|
||||||
{ _blkNum :: !Int
|
|
||||||
, _blkNumLast :: !Int
|
|
||||||
, _timeLast :: !TimeSpec
|
|
||||||
}
|
|
||||||
deriving stock (Typeable,Generic)
|
|
||||||
|
|
||||||
makeLenses 'Stats
|
|
||||||
|
|
||||||
instance Default (Stats e) where
|
|
||||||
def = Stats 0 0 0
|
|
||||||
|
|
||||||
newStatsIO :: MonadIO m => m (Stats e)
|
|
||||||
newStatsIO = pure $ Stats 0 0 0
|
|
||||||
|
|
||||||
type instance SessionData e (Stats e) = Stats e
|
|
||||||
|
|
||||||
instance Serialise TimeSpec
|
|
||||||
instance Serialise (Stats e)
|
|
||||||
|
|
||||||
data instance SessionKey e (Stats e) = StatsKey
|
|
||||||
deriving stock (Typeable,Eq)
|
|
||||||
|
|
||||||
instance Typeable (SessionKey e (Stats e)) => Hashable (SessionKey e (Stats e)) where
|
|
||||||
hashWithSalt salt _ = hashWithSalt salt (someTypeRep p)
|
|
||||||
where
|
|
||||||
p = Proxy @(SessionKey e (Stats e))
|
|
||||||
|
|
||||||
|
|
||||||
newtype Speed = Speed (Fixed E1)
|
|
||||||
deriving newtype (Ord, Eq, Num, Real, Fractional, Show)
|
|
||||||
|
|
||||||
instance Pretty Speed where
|
|
||||||
pretty (Speed n) = pretty (show n)
|
|
||||||
|
|
||||||
|
|
||||||
updateStats :: forall e m . (MonadIO m, Sessions e (Stats e) m)
|
|
||||||
=> Bool -> Int -> m (Stats e)
|
|
||||||
|
|
||||||
updateStats updTime blknum = do
|
|
||||||
de <- newStatsIO
|
|
||||||
stats <- fetch @e True de StatsKey id
|
|
||||||
|
|
||||||
t <- if updTime then do
|
|
||||||
liftIO $ getTime Monotonic
|
|
||||||
else
|
|
||||||
pure (view timeLast stats)
|
|
||||||
|
|
||||||
let blkNumNew = view blkNum stats + blknum
|
|
||||||
|
|
||||||
let blast = if updTime then
|
|
||||||
blkNumNew
|
|
||||||
else
|
|
||||||
view blkNumLast stats
|
|
||||||
|
|
||||||
let newStats = set blkNum blkNumNew
|
|
||||||
. set timeLast t
|
|
||||||
. set blkNumLast blast
|
|
||||||
$ stats
|
|
||||||
|
|
||||||
update @e de StatsKey (const newStats)
|
|
||||||
|
|
||||||
pure newStats
|
|
||||||
|
|
||||||
data DownloadEnv e =
|
|
||||||
DownloadEnv
|
|
||||||
{ _downloadQ :: TQueue (Hash HbSync)
|
|
||||||
, _peerBusy :: TVar (HashMap (Peer e) ())
|
|
||||||
}
|
|
||||||
|
|
||||||
makeLenses 'DownloadEnv
|
|
||||||
|
|
||||||
class (Eq (Peer e), Hashable (Peer e), Pretty (Peer e)) => MyPeer e
|
|
||||||
instance (Eq (Peer e), Hashable (Peer e), Pretty (Peer e)) => MyPeer e
|
|
||||||
|
|
||||||
newDownloadEnv :: (MonadIO m, MyPeer e) => m (DownloadEnv e)
|
|
||||||
newDownloadEnv = liftIO do
|
|
||||||
DownloadEnv <$> newTQueueIO
|
|
||||||
<*> newTVarIO mempty
|
|
||||||
|
|
||||||
newtype BlockDownloadM e m a =
|
|
||||||
BlockDownloadM { fromBlockDownloadM :: ReaderT (DownloadEnv e) m a }
|
|
||||||
deriving newtype ( Functor
|
|
||||||
, Applicative
|
|
||||||
, Monad
|
|
||||||
, MonadIO
|
|
||||||
, MonadReader (DownloadEnv e)
|
|
||||||
, MonadTrans
|
|
||||||
)
|
|
||||||
|
|
||||||
runDownloadM :: (MyPeer e, MonadIO m) => BlockDownloadM e m a -> m a
|
|
||||||
runDownloadM m = runReaderT ( fromBlockDownloadM m ) =<< newDownloadEnv
|
|
||||||
|
|
||||||
withDownload :: (MyPeer e, MonadIO m) => DownloadEnv e -> BlockDownloadM e m a -> m a
|
|
||||||
withDownload e m = runReaderT ( fromBlockDownloadM m ) e
|
|
||||||
|
|
||||||
addDownload :: MonadIO m => Hash HbSync -> BlockDownloadM e m ()
|
|
||||||
addDownload h = do
|
|
||||||
q <- asks (view downloadQ)
|
|
||||||
liftIO $ atomically $ writeTQueue q h
|
|
||||||
-- debug $ "addDownload" <+> pretty h
|
|
||||||
-- pause ( 0.25 :: Timeout 'Seconds )
|
|
||||||
|
|
||||||
withFreePeer :: (MyPeer e, MonadIO m)
|
|
||||||
=> Peer e
|
|
||||||
-> BlockDownloadM e m ()
|
|
||||||
-> BlockDownloadM e m ()
|
|
||||||
-> BlockDownloadM e m ()
|
|
||||||
|
|
||||||
withFreePeer p n m = do
|
|
||||||
busy <- asks (view peerBusy)
|
|
||||||
avail <- liftIO $ atomically
|
|
||||||
$ stateTVar busy $
|
|
||||||
\s -> case HashMap.lookup p s of
|
|
||||||
Nothing -> (True, HashMap.insert p () s)
|
|
||||||
Just{} -> (False, s)
|
|
||||||
if not avail
|
|
||||||
then n
|
|
||||||
else do
|
|
||||||
r <- m
|
|
||||||
liftIO $ atomically $ modifyTVar busy $ HashMap.delete p
|
|
||||||
pure r
|
|
||||||
|
|
||||||
getBlockForDownload :: MonadIO m => BlockDownloadM e m (Hash HbSync)
|
|
||||||
getBlockForDownload = do
|
|
||||||
q <- asks (view downloadQ)
|
|
||||||
liftIO $ atomically $ readTQueue q
|
|
||||||
|
|
||||||
processBlock :: forall e m . ( MonadIO m
|
|
||||||
, HasStorage m
|
|
||||||
, Block ByteString ~ ByteString
|
|
||||||
)
|
|
||||||
=> Hash HbSync
|
|
||||||
-> BlockDownloadM e m ()
|
|
||||||
|
|
||||||
processBlock h = do
|
|
||||||
|
|
||||||
sto <- lift getStorage
|
|
||||||
|
|
||||||
bt <- liftIO $ getBlock sto h <&> fmap (tryDetect h)
|
|
||||||
|
|
||||||
case bt of
|
|
||||||
Nothing -> addDownload h
|
|
||||||
|
|
||||||
Just (AnnRef{}) -> pure ()
|
|
||||||
|
|
||||||
Just (Merkle{}) -> do
|
|
||||||
debug $ "GOT MERKLE. requesting nodes/leaves" <+> pretty h
|
|
||||||
walkMerkle h (liftIO . getBlock sto) $ \(hr :: [HashRef]) -> do
|
|
||||||
|
|
||||||
for_ hr $ \(HashRef blk) -> do
|
|
||||||
|
|
||||||
-- debug $ pretty blk
|
|
||||||
|
|
||||||
here <- liftIO (hasBlock sto blk) <&> isJust
|
|
||||||
|
|
||||||
if here then do
|
|
||||||
debug $ "block" <+> pretty blk <+> "is already here"
|
|
||||||
pure () -- we don't need to recurse, cause walkMerkle is recursing for us
|
|
||||||
|
|
||||||
else
|
|
||||||
addDownload blk
|
|
||||||
|
|
||||||
Just (Blob{}) -> do
|
|
||||||
pure ()
|
|
||||||
|
|
||||||
|
|
||||||
downloadFromWithPeer :: forall e m . ( MyPeer e
|
|
||||||
, MonadIO m
|
|
||||||
, Request e (BlockInfo e) m
|
|
||||||
, Request e (BlockChunks e) m
|
|
||||||
, MonadReader (PeerEnv e ) m
|
|
||||||
, PeerMessaging e
|
|
||||||
, HasProtocol e (BlockInfo e)
|
|
||||||
, EventListener e (BlockInfo e) m
|
|
||||||
, EventListener e (BlockChunks e) m
|
|
||||||
, Sessions e (BlockChunks e) m
|
|
||||||
, Sessions e (PeerInfo e) m
|
|
||||||
, Block ByteString ~ ByteString
|
|
||||||
, HasStorage m
|
|
||||||
)
|
|
||||||
=> Peer e
|
|
||||||
-> Hash HbSync
|
|
||||||
-> BlockDownloadM e m ()
|
|
||||||
downloadFromWithPeer peer h = do
|
|
||||||
|
|
||||||
|
|
||||||
npi <- newPeerInfo
|
|
||||||
pinfo <- lift $ fetch True npi (PeerInfoKey peer) id
|
|
||||||
|
|
||||||
waitSize <- liftIO $ newTBQueueIO 1
|
|
||||||
|
|
||||||
lift $ do
|
|
||||||
subscribe @e (BlockSizeEventKey h) $ \(BlockSizeEvent (p1,hx,s)) -> do
|
|
||||||
when ( p1 == peer ) $ do
|
|
||||||
liftIO $ atomically $ writeTBQueue waitSize s
|
|
||||||
|
|
||||||
request @e peer (GetBlockSize @e h)
|
|
||||||
|
|
||||||
esize <- liftIO $ race ( pause defBlockInfoTimeout ) do -- FIXME: block size wait time
|
|
||||||
atomically $ readTBQueue waitSize
|
|
||||||
|
|
||||||
let mbSize = either (const Nothing) Just esize
|
|
||||||
|
|
||||||
sto <- lift $ getStorage
|
|
||||||
|
|
||||||
case mbSize of
|
|
||||||
Nothing -> void $ addDownload h
|
|
||||||
Just thisBkSize -> do
|
|
||||||
|
|
||||||
coo <- genCookie (peer,h)
|
|
||||||
let key = DownloadSessionKey (peer, coo)
|
|
||||||
let chusz = defChunkSize
|
|
||||||
dnwld <- newBlockDownload h
|
|
||||||
let chuQ = view sBlockChunks dnwld
|
|
||||||
let new = set sBlockChunkSize chusz
|
|
||||||
. set sBlockSize (fromIntegral thisBkSize)
|
|
||||||
$ dnwld
|
|
||||||
|
|
||||||
lift $ update @e new key id
|
|
||||||
|
|
||||||
let burstSizeT = view peerBurst pinfo
|
|
||||||
|
|
||||||
burstSize <- liftIO $ readTVarIO burstSizeT
|
|
||||||
|
|
||||||
let offsets = calcChunks thisBkSize (fromIntegral chusz) :: [(Offset, Size)]
|
|
||||||
|
|
||||||
let chunkNums = [ 0 .. pred (length offsets) ]
|
|
||||||
|
|
||||||
let bursts = calcBursts burstSize chunkNums
|
|
||||||
|
|
||||||
-- debug $ "bursts: " <+> pretty bursts
|
|
||||||
|
|
||||||
r <- liftIO $ newTVarIO (mempty :: IntMap ByteString)
|
|
||||||
rq <- liftIO newTQueueIO
|
|
||||||
|
|
||||||
for_ bursts $ liftIO . atomically . writeTQueue rq
|
|
||||||
|
|
||||||
fix \next -> do
|
|
||||||
burst <- liftIO $ atomically $ tryReadTQueue rq
|
|
||||||
|
|
||||||
case burst of
|
|
||||||
|
|
||||||
Just (i,chunksN) -> do
|
|
||||||
let req = BlockGetChunks h chusz (fromIntegral i) (fromIntegral chunksN)
|
|
||||||
lift $ request peer (BlockChunks @e coo req)
|
|
||||||
|
|
||||||
-- TODO: here wait for all requested chunks!
|
|
||||||
-- FIXME: it may blocks forever, so must be timeout and retry
|
|
||||||
|
|
||||||
catched <- either id id <$> liftIO ( race ( pause defChunkWaitMax >> pure mempty )
|
|
||||||
( replicateM chunksN
|
|
||||||
$ atomically
|
|
||||||
$ readTQueue chuQ )
|
|
||||||
|
|
||||||
)
|
|
||||||
when (null catched) $ do
|
|
||||||
|
|
||||||
-- nerfing peer burst size.
|
|
||||||
-- FIXME: we need a thread that will be reset them again
|
|
||||||
|
|
||||||
newBurst <- liftIO $ atomically
|
|
||||||
$ stateTVar burstSizeT $ \c -> let v = max 1 (c `div` 2)
|
|
||||||
in (v,v)
|
|
||||||
|
|
||||||
let chuchu = calcBursts newBurst [ i + n | n <- [0 .. chunksN] ]
|
|
||||||
|
|
||||||
debug $ "new burst: " <+> pretty newBurst
|
|
||||||
debug $ "missed chunks for request" <+> pretty (i,chunksN)
|
|
||||||
|
|
||||||
for_ chuchu $ liftIO . atomically . writeTQueue rq
|
|
||||||
|
|
||||||
for_ catched $ \(num,bs) -> do
|
|
||||||
liftIO $ atomically $ modifyTVar' r (IntMap.insert (fromIntegral num) bs)
|
|
||||||
|
|
||||||
next
|
|
||||||
|
|
||||||
Nothing -> do
|
|
||||||
|
|
||||||
sz <- liftIO $ readTVarIO r <&> IntMap.size
|
|
||||||
|
|
||||||
if sz == length offsets then do
|
|
||||||
pieces <- liftIO $ readTVarIO r <&> IntMap.elems
|
|
||||||
let block = mconcat pieces
|
|
||||||
let h1 = hashObject @HbSync block
|
|
||||||
|
|
||||||
if h1 == h then do
|
|
||||||
-- debug "PROCESS BLOCK"
|
|
||||||
lift $ expire @e key
|
|
||||||
void $ liftIO $ putBlock sto block
|
|
||||||
void $ processBlock h
|
|
||||||
else do
|
|
||||||
debug "HASH NOT MATCH"
|
|
||||||
debug "MAYBE THAT PEER IS JERK"
|
|
||||||
|
|
||||||
else do
|
|
||||||
debug "RETRY BLOCK DOWNLOADING / ASK FOR MISSED CHUNKS"
|
|
||||||
got <- liftIO $ readTVarIO r <&> IntMap.keysSet
|
|
||||||
let need = IntSet.fromList (fmap fromIntegral chunkNums)
|
|
||||||
|
|
||||||
let missed = IntSet.toList $ need `IntSet.difference` got
|
|
||||||
|
|
||||||
-- normally this should not happen
|
|
||||||
-- however, let's try do download the tails
|
|
||||||
-- by one chunk a time
|
|
||||||
for_ missed $ \n -> do
|
|
||||||
liftIO $ atomically $ writeTQueue rq (n,1)
|
|
||||||
|
|
||||||
|
|
||||||
instance HasPeerLocator e m => HasPeerLocator e (BlockDownloadM e m) where
|
|
||||||
getPeerLocator = lift getPeerLocator
|
|
||||||
|
|
||||||
blockDownloadLoop :: forall e m . ( m ~ PeerM e IO
|
|
||||||
, MonadIO m
|
|
||||||
, Request e (BlockInfo e) m
|
|
||||||
, Request e (BlockAnnounce e) m
|
|
||||||
, HasProtocol e (BlockInfo e)
|
|
||||||
, HasProtocol e (BlockAnnounce e)
|
|
||||||
, HasProtocol e (BlockChunks e)
|
|
||||||
, EventListener e (BlockInfo e) m
|
|
||||||
, EventListener e (BlockChunks e) m
|
|
||||||
, EventListener e (BlockAnnounce e) m
|
|
||||||
, EventEmitter e (BlockChunks e) m
|
|
||||||
, Sessions e (BlockInfo e) m
|
|
||||||
, Sessions e (BlockChunks e) m
|
|
||||||
, Sessions e (PeerInfo e) m
|
|
||||||
, PeerSessionKey e (PeerInfo e)
|
|
||||||
, Typeable (SessionKey e (BlockChunks e))
|
|
||||||
, Typeable (SessionKey e (BlockInfo e))
|
|
||||||
, HasStorage m
|
|
||||||
, Pretty (Peer e)
|
|
||||||
, Block ByteString ~ ByteString
|
|
||||||
, PeerMessaging e
|
|
||||||
)
|
|
||||||
=> m ()
|
|
||||||
blockDownloadLoop = do
|
|
||||||
|
|
||||||
e <- ask
|
|
||||||
stor <- getStorage
|
|
||||||
|
|
||||||
let blks = [ "GTtQp6QjK7G9Sh5Aq4koGSpMX398WRWn3DV28NUAYARg"
|
|
||||||
, "ECWYwWXiLgNvCkN1EFpSYqsPcWfnL4bAQADsyZgy1Cbr"
|
|
||||||
]
|
|
||||||
|
|
||||||
pl <- getPeerLocator @e
|
|
||||||
|
|
||||||
-- TODO: peer info loop
|
|
||||||
void $ liftIO $ async $ forever $ withPeerM e $ do
|
|
||||||
pause @'Seconds 20
|
|
||||||
pee <- knownPeers @e pl
|
|
||||||
|
|
||||||
npi <- newPeerInfo
|
|
||||||
|
|
||||||
for_ pee $ \p -> do
|
|
||||||
pinfo <- fetch True npi (PeerInfoKey p) id
|
|
||||||
burst <- liftIO $ readTVarIO (view peerBurst pinfo)
|
|
||||||
debug $ "peer" <+> pretty p <+> "burst: " <+> pretty burst
|
|
||||||
pure ()
|
|
||||||
|
|
||||||
runDownloadM @e $ do
|
|
||||||
|
|
||||||
env <- ask
|
|
||||||
|
|
||||||
let again h = do
|
|
||||||
debug $ "block fucked: " <+> pretty h
|
|
||||||
withPeerM e $ withDownload env (addDownload h)
|
|
||||||
|
|
||||||
mapM_ processBlock blks
|
|
||||||
|
|
||||||
fix \next -> do
|
|
||||||
|
|
||||||
h <- getBlockForDownload
|
|
||||||
|
|
||||||
here <- liftIO $ hasBlock stor h <&> isJust
|
|
||||||
|
|
||||||
unless here do
|
|
||||||
|
|
||||||
void $ runMaybeT $ do
|
|
||||||
p <- MaybeT $ knownPeers @e pl >>= liftIO . shuffleM <&> headMay
|
|
||||||
|
|
||||||
liftIO $ race ( pause defBlockWaitMax >> again h ) do
|
|
||||||
withPeerM e $ withDownload env $ do -- NOTE: really crazy shit
|
|
||||||
withFreePeer p (addDownload h >> pause (0.1 :: Timeout 'Seconds)) do
|
|
||||||
downloadFromWithPeer p h
|
|
||||||
|
|
||||||
next
|
|
||||||
|
|
||||||
|
|
||||||
-- NOTE: this is an adapter for a ResponseM monad
|
|
||||||
-- because response is working in ResponseM monad (ha!)
|
|
||||||
-- So don't be confused with types
|
|
||||||
--
|
|
||||||
mkAdapter :: forall e m . ( m ~ PeerM e IO
|
|
||||||
, HasProtocol e (BlockChunks e)
|
|
||||||
, Hashable (SessionKey e (BlockChunks e))
|
|
||||||
, Sessions e (BlockChunks e) (ResponseM e m)
|
|
||||||
, Typeable (SessionKey e (BlockChunks e))
|
|
||||||
, Default (SessionData e (Stats e))
|
|
||||||
, EventEmitter e (BlockChunks e) m
|
|
||||||
, Pretty (Peer e)
|
|
||||||
, Block ByteString ~ ByteString
|
|
||||||
)
|
|
||||||
=> m (BlockChunksI e (ResponseM e m ))
|
|
||||||
mkAdapter = do
|
|
||||||
storage <- getStorage
|
|
||||||
pure $
|
|
||||||
BlockChunksI
|
|
||||||
{ blkSize = liftIO . hasBlock storage
|
|
||||||
, blkChunk = \h o s -> liftIO (getChunk storage h o s)
|
|
||||||
, blkGetHash = \c -> find (DownloadSessionKey @e c) (view sBlockHash)
|
|
||||||
|
|
||||||
, blkAcceptChunk = \(c,p,h,n,bs) -> void $ runMaybeT $ do
|
|
||||||
|
|
||||||
-- debug "AAAA!"
|
|
||||||
|
|
||||||
let cKey = DownloadSessionKey (p,c)
|
|
||||||
|
|
||||||
-- check if there is a session
|
|
||||||
-- FIXME:
|
|
||||||
-- TODO: log situation when no session
|
|
||||||
|
|
||||||
ddd <- lift $ find cKey id
|
|
||||||
|
|
||||||
when (isNothing ddd) $ do
|
|
||||||
debug "SESSION NOT FOUND!"
|
|
||||||
|
|
||||||
dwnld <- MaybeT $ find cKey (view sBlockChunks)
|
|
||||||
|
|
||||||
liftIO $ atomically $ writeTQueue dwnld (n, bs)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = do
|
|
||||||
hSetBuffering stderr LineBuffering
|
|
||||||
|
|
||||||
void $ race (pause (600 :: Timeout 'Seconds)) $ do
|
|
||||||
|
|
||||||
-- fake <- newFakeP2P True <&> Fabriq
|
|
||||||
|
|
||||||
udp0 <- newMessagingUDP False (Just "127.0.0.1:10001") `orDie` "Can't start listener on 10001"
|
|
||||||
udp1 <- newMessagingUDP False (Just "127.0.0.1:10002") `orDie` "Can't start listener on 10002"
|
|
||||||
|
|
||||||
let (p0:ps) = [getOwnPeer udp0, getOwnPeer udp1]
|
|
||||||
|
|
||||||
-- others
|
|
||||||
others <- forM ps $ \p -> async $ runTestPeer udp1 p $ \s cw -> do
|
|
||||||
let findBlk = hasBlock s
|
|
||||||
|
|
||||||
runPeerM (AnyStorage s) (Fabriq udp1) p $ do
|
|
||||||
adapter <- mkAdapter
|
|
||||||
|
|
||||||
runProto @Fake
|
|
||||||
[ makeResponse (blockSizeProto findBlk dontHandle)
|
|
||||||
, makeResponse (blockChunksProto adapter)
|
|
||||||
, makeResponse blockAnnounceProto
|
|
||||||
]
|
|
||||||
|
|
||||||
our <- async $ runTestPeer udp0 p0 $ \s cw -> do
|
|
||||||
let blk = hasBlock s
|
|
||||||
|
|
||||||
-- void $ async $ forever $ do
|
|
||||||
-- pause ( 1 :: Timeout 'Seconds )
|
|
||||||
-- wip <- blocksInProcess cw
|
|
||||||
-- debug $ "blocks wip:" <+> pretty wip
|
|
||||||
|
|
||||||
runPeerM (AnyStorage s) (Fabriq udp0) p0 $ do
|
|
||||||
adapter <- mkAdapter
|
|
||||||
env <- ask
|
|
||||||
|
|
||||||
pl <- getPeerLocator @Fake
|
|
||||||
|
|
||||||
addPeers @Fake pl ps
|
|
||||||
|
|
||||||
as <- liftIO $ async $ withPeerM env blockDownloadLoop
|
|
||||||
|
|
||||||
me <- liftIO $ replicateM 1 $ async $ liftIO $ withPeerM env $ do
|
|
||||||
runProto @Fake
|
|
||||||
[ makeResponse (blockSizeProto blk handleBlockInfo)
|
|
||||||
, makeResponse (blockChunksProto adapter)
|
|
||||||
, makeResponse blockAnnounceProto
|
|
||||||
]
|
|
||||||
|
|
||||||
liftIO $ mapM_ wait me
|
|
||||||
|
|
||||||
liftIO $ cancel as
|
|
||||||
|
|
||||||
pause ( 599.9 :: Timeout 'Seconds )
|
|
||||||
|
|
||||||
mapM_ cancel (our:others)
|
|
||||||
|
|
||||||
(_, e) <- waitAnyCatchCancel (our:others)
|
|
||||||
|
|
||||||
debug (pretty $ show e)
|
|
||||||
debug "we're done"
|
|
||||||
assertBool "success" True
|
|
||||||
exitSuccess
|
|
||||||
|
|
||||||
assertBool "failed" False
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import HBS2.System.Logger.Simple
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Concurrent.Async
|
||||||
|
|
||||||
|
import System.Log.FastLogger
|
||||||
|
import Prettyprinter
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
withSimpleLogger do
|
||||||
|
replicateConcurrently_ 1000 do
|
||||||
|
debug $ "DEBUG" <+> pretty 1000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue