Skip to content
Snippets Groups Projects
Commit 3428199c authored by Jean-Paul Calderone's avatar Jean-Paul Calderone
Browse files

Split `download` into more manageable pieces

parent d6fe1452
Branches
Tags
1 merge request!2Incorporate tahoe-great-black-swamp to do share downloads using GBS
Pipeline #4517 failed
......@@ -16,7 +16,8 @@ module Tahoe.Download (
import Control.Exception (throwIO)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Data.Binary (decodeOrFail)
import Data.Binary (Word16, decodeOrFail)
import qualified Data.ByteString as B
import Data.ByteString.Base32 (encodeBase32Unpadded)
import Data.ByteString.Base64 (encodeBase64)
import qualified Data.ByteString.Lazy as LB
......@@ -83,10 +84,9 @@ data DiscoverError
| StorageServerUnreachable LookupError
deriving (Eq, Ord, Show)
-- TODO The result might need to be in IO in case the URL indicates a
-- Tor-based route to the server. In this case we might need to launch a Tor
-- daemon or connect to a running Tor daemon or at least set up a new Tor
-- circuit. All of which require I/O. But we can always refactor later!
{- | The type of a function that can produce a concrete StorageServer from
that server's announcement.
-}
type LookupServer m = StorageServerAnnouncement -> m (Either LookupError StorageServer)
{- | Recover the application data associated with a given capability from the
......@@ -104,7 +104,31 @@ download ::
-- | Either a description of how the recovery failed or the recovered
-- application data.
m (Either DownloadError LB.ByteString)
download servers cap@Reader{readKey, verifier = Verifier{..}} lookupServer =
download servers cap@Reader{verifier = Verifier{..}} lookupServer = do
locationE <- locateShares servers lookupServer storageIndex required
case locationE of
Left err -> pure $ Left err
Right discovered -> do
-- XXX note shares can contain failures
shares <- fetchShares storageIndex discovered
decodeShares cap shares
-- | Find out which servers claim to have shares related to a given storage index.
locateShares ::
MonadIO m =>
-- | Information about the servers from which to consider downloading shares
-- representing the application data.
Map.Map StorageServerID StorageServerAnnouncement ->
-- | Get functions for interacting with a server given its URL.
LookupServer m ->
-- | The storage index about which to retrieve information.
B.ByteString ->
-- | The number of shares we need to locate. If we cannot find at least
-- this many shares the result will be an error.
Word16 ->
-- | Either an error or a guide to where shares are placed.
m (Either DownloadError [(StorageServer, Set.Set ShareNum)])
locateShares servers lookupServer storageIndex required =
case Map.toList servers of
[] -> pure . Left $ NoConfiguredServers
serverList -> do
......@@ -118,24 +142,46 @@ download servers cap@Reader{readKey, verifier = Verifier{..}} lookupServer =
else
if (fromIntegral required >) . countDistinctShares $ discovered
then pure $ Left NotEnoughShares{notEnoughSharesNeeded = fromIntegral required, notEnoughSharesFound = countDistinctShares discovered}
else do
-- XXX up to here is probably "locateShares". now we move in to "fetchShares".
let sharemap = makeShareMap discovered
-- XXX note shares can contain failures
shares <- mapM (uncurry $ downloadShare storageIndex) (Map.toList sharemap)
-- XXX That was brief. Probably missing some complexity. now we move on to "decodeShares".
-- Filter down to shares we actually got.
let someShares = filter (isRight . snd) shares
fewerShares = filter (isRight . snd) $ (\(sharenum, Right bs) -> (sharenum, decodeOrFail bs)) <$> someShares
onlyDecoded = (\(sharenum, Right (_, _, share)) -> (fromIntegral sharenum, share)) <$> fewerShares
if length onlyDecoded < fromIntegral required
then pure $ Left NotEnoughDecodedShares{notEnoughDecodedSharesNeeded = fromIntegral required, notEnoughDecodedSharesFound = length onlyDecoded}
else do
ciphertext <- liftIO $ Tahoe.CHK.decode cap onlyDecoded
case ciphertext of
Nothing -> pure $ Left ShareDecodingFailed
Just ct ->
pure . Right $ Tahoe.CHK.Encrypt.decrypt readKey ct
else pure $ Right discovered
{- | Given a guide to where shares for a given storage index are placed,
download them.
-}
fetchShares ::
MonadIO m =>
-- | The storage index of the shares to download.
B.ByteString ->
-- | The guide to where shares are placed.
[(StorageServer, Set.Set ShareNum)] ->
m [(ShareNum, Either DownloadError LB.ByteString)]
fetchShares storageIndex discovered =
mapM (uncurry $ downloadShare storageIndex) (Map.toList sharemap)
where
sharemap = makeShareMap discovered
{- | Given the results of downloading shares related to a given capability,
decode them and decrypt the contents of possible.
-}
decodeShares ::
MonadIO m =>
-- | The read capability which allows the contents to be decrypted.
Reader ->
-- | The results of downloading the shares.
[(ShareNum, Either DownloadError LB.ByteString)] ->
m (Either DownloadError LB.ByteString)
decodeShares cap@Reader{readKey, verifier = Verifier{..}} shares =
-- Filter down to shares we actually got.
let someShares = filter (isRight . snd) shares
fewerShares = filter (isRight . snd) $ (\(sharenum, Right bs) -> (sharenum, decodeOrFail bs)) <$> someShares
onlyDecoded = (\(sharenum, Right (_, _, share)) -> (fromIntegral sharenum, share)) <$> fewerShares
in if length onlyDecoded < fromIntegral required
then pure $ Left NotEnoughDecodedShares{notEnoughDecodedSharesNeeded = fromIntegral required, notEnoughDecodedSharesFound = length onlyDecoded}
else do
ciphertext <- liftIO $ Tahoe.CHK.decode cap onlyDecoded
case ciphertext of
Nothing -> pure $ Left ShareDecodingFailed
Just ct ->
pure . Right $ Tahoe.CHK.Encrypt.decrypt readKey ct
{- | Figure the total number of distinct shares reported by all of the servers
we asked.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment