diff --git a/src/Tahoe/Download.hs b/src/Tahoe/Download.hs index b2b394fbc42459c39b9eeba03661f6f342e634e6..f5a05d6a3f33cf0aa1af124657913dccb1d3a678 100644 --- a/src/Tahoe/Download.hs +++ b/src/Tahoe/Download.hs @@ -2,6 +2,7 @@ module Tahoe.Download (DownloadError (..), download) where import qualified Data.ByteString.Lazy as LB import Data.Either (rights) +import Data.List (foldl') import qualified Data.Map.Strict as Map import qualified Data.Set as Set import qualified Data.Text as T @@ -49,13 +50,13 @@ download servers Reader{verifier = Verifier{..}} openServer = if null discovered then pure $ Left NoReachableServers else - if (fromIntegral required >=) . countShares $ discovered - then pure $ Left NotEnoughShares{notEnoughSharesNeeded = fromIntegral required, notEnoughSharesFound = countShares discovered} + if (fromIntegral required >=) . countDistinctShares $ discovered + then pure $ Left NotEnoughShares{notEnoughSharesNeeded = fromIntegral required, notEnoughSharesFound = countDistinctShares discovered} else pure . Right . LB.fromStrict . encodeUtf8 . T.pack . show $ discovered where - -- Figure the total number of shares reported by all of the servers we + -- Figure the total number of distinct shares reported by all of the servers we -- asked. - countShares = sum . map (Set.size . snd) + countDistinctShares = Set.size . foldl' Set.union mempty . map snd -- Ask one server which shares it has related to the storage index in -- question. diff --git a/test/Spec.hs b/test/Spec.hs index 42c875383dbcc729d0713e72d834fc502d351578..74566683bf79262165d72f1c34f878985009d099 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -73,6 +73,55 @@ tests = then pure . pure $ server else pure Nothing + -- Try to download the cap which requires three shares to reconstruct. + result <- liftIO $ download anns cap openServer + assertEqual + "download should fail with not enough shares" + (Left NotEnoughShares{notEnoughSharesNeeded = 3, notEnoughSharesFound = 2}) + result + , testCase "not enough distinct shares" $ do + -- If we can't recover enough *distinct* shares from the + -- configured servers then we can't possibly get enough shares to + -- recover the application data. Duplicate shares do us no good. + let anns = + Map.fromList + [ + ( "v0-abc123" + , StorageServerAnnouncement + { storageServerAnnouncementFURL = Just "somewhere" + , storageServerAnnouncementNick = Just "abc123" + , storageServerAnnouncementPermutationSeed = Nothing + } + ) + , + ( "v0-abc456" + , StorageServerAnnouncement + { storageServerAnnouncementFURL = Just "elsewhere" + , storageServerAnnouncementNick = Just "abc123" + , storageServerAnnouncementPermutationSeed = Nothing + } + ) + ] + cap = trivialCap 3 3 + + -- Three shares exist + somewhere <- memoryStorageServer + let idx = storageIndex . verifier $ cap + offset = 0 + storageServerWrite somewhere idx 0 offset "Hello world" + storageServerWrite somewhere idx 1 offset "Hello world" + -- But this one is just a duplicate of share 0 on the other + -- server. + elsewhere <- memoryStorageServer + storageServerWrite elsewhere idx 0 offset "Hello world" + + -- Make the server reachable. + let openServer furl = + pure $ case furl of + "somewhere" -> pure somewhere + "elsewhere" -> pure elsewhere + _ -> Nothing + -- Try to download the cap which requires three shares to reconstruct. result <- liftIO $ download anns cap openServer assertEqual