module LibP2P.DHT.API
(
provide
, putValue
, findProviders
) where
import Control.Concurrent.Async (mapConcurrently)
import Control.Exception (SomeException, catch)
import Control.Monad (void)
import Data.ByteString (ByteString)
import Data.Time (getCurrentTime)
import Data.Time.Format.ISO8601 (iso8601Show)
import qualified Data.Text as T
import LibP2P.Crypto.PeerId (PeerId (..), peerIdBytes)
import LibP2P.DHT
( DHTNode (..)
, ProviderEntry (..)
, Validator (..)
, addProvider
, storeRecord
)
import LibP2P.DHT.Lookup (iterativeFindNode, iterativeGetProviders)
import LibP2P.DHT.Message
import LibP2P.DHT.Types (BucketEntry (..), ConnectionType (..), entryPeerId, kValue)
import LibP2P.Multiaddr (Multiaddr, toBytes)
provide :: DHTNode -> [Multiaddr] -> ByteString -> IO ()
provide :: DHTNode -> [Multiaddr] -> ByteString -> IO ()
provide DHTNode
node [Multiaddr]
addrs ByteString
key = do
closest <- DHTNode -> PeerId -> IO [BucketEntry]
iterativeFindNode DHTNode
node (ByteString -> PeerId
PeerId ByteString
key)
let providerPeer = ByteString -> [ByteString] -> ConnectionType -> DHTPeer
DHTPeer (PeerId -> ByteString
peerIdBytes (DHTNode -> PeerId
dhtLocalPeerId DHTNode
node))
((Multiaddr -> ByteString) -> [Multiaddr] -> [ByteString]
forall a b. (a -> b) -> [a] -> [b]
map Multiaddr -> ByteString
toBytes [Multiaddr]
addrs)
ConnectionType
Connected
providerMsg = DHTMessage
emptyDHTMessage
{ msgType = AddProvider
, msgKey = key
, msgProviderPeers = [providerPeer]
}
void $ mapConcurrently (\BucketEntry
entry ->
(DHTNode -> PeerId -> DHTMessage -> IO (Either [Char] DHTMessage)
dhtSendRequest DHTNode
node) (BucketEntry -> PeerId
entryPeerId BucketEntry
entry) DHTMessage
providerMsg
IO (Either [Char] DHTMessage)
-> (SomeException -> IO (Either [Char] DHTMessage))
-> IO (Either [Char] DHTMessage)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` (\(SomeException
_ :: SomeException) -> Either [Char] DHTMessage -> IO (Either [Char] DHTMessage)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] DHTMessage
forall a b. a -> Either a b
Left [Char]
"send failed"))
) (take kValue closest)
now <- getCurrentTime
addProvider node key
ProviderEntry
{ peProvider = dhtLocalPeerId node
, peAddrs = addrs
, peTimestamp = now
}
putValue :: DHTNode -> Validator -> ByteString -> ByteString -> IO (Either String ())
putValue :: DHTNode
-> Validator -> ByteString -> ByteString -> IO (Either [Char] ())
putValue DHTNode
node Validator
validator ByteString
key ByteString
value = do
case Validator -> ByteString -> ByteString -> Either [Char] ()
valValidate Validator
validator ByteString
key ByteString
value of
Left [Char]
err -> Either [Char] () -> IO (Either [Char] ())
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] ()
forall a b. a -> Either a b
Left ([Char] -> Either [Char] ()) -> [Char] -> Either [Char] ()
forall a b. (a -> b) -> a -> b
$ [Char]
"value validation failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
err)
Right () -> do
closest <- DHTNode -> PeerId -> IO [BucketEntry]
iterativeFindNode DHTNode
node (ByteString -> PeerId
PeerId ByteString
key)
now <- getCurrentTime
let record = ByteString -> ByteString -> Text -> DHTRecord
DHTRecord ByteString
key ByteString
value ([Char] -> Text
T.pack (UTCTime -> [Char]
forall t. ISO8601 t => t -> [Char]
iso8601Show UTCTime
now))
putMsg = DHTMessage
emptyDHTMessage
{ msgType = PutValue
, msgKey = key
, msgRecord = Just record
}
void $ mapConcurrently (\BucketEntry
entry ->
(DHTNode -> PeerId -> DHTMessage -> IO (Either [Char] DHTMessage)
dhtSendRequest DHTNode
node) (BucketEntry -> PeerId
entryPeerId BucketEntry
entry) DHTMessage
putMsg
IO (Either [Char] DHTMessage)
-> (SomeException -> IO (Either [Char] DHTMessage))
-> IO (Either [Char] DHTMessage)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` (\(SomeException
_ :: SomeException) -> Either [Char] DHTMessage -> IO (Either [Char] DHTMessage)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] DHTMessage
forall a b. a -> Either a b
Left [Char]
"send failed"))
) (take kValue closest)
storeRecord node record
pure (Right ())
findProviders :: DHTNode -> ByteString -> IO [ProviderEntry]
findProviders :: DHTNode -> ByteString -> IO [ProviderEntry]
findProviders DHTNode
node ByteString
key = DHTNode -> ByteString -> IO [ProviderEntry]
iterativeGetProviders DHTNode
node ByteString
key