mirror of https://github.com/voidlizard/hbs2
fsck
This commit is contained in:
parent
61e47be38f
commit
8eae06b2af
|
@ -147,6 +147,15 @@ catFromMerkle getB =
|
||||||
. Q.fromChunks
|
. Q.fromChunks
|
||||||
. (S.map cs . streamCatFromMerkle getB)
|
. (S.map cs . streamCatFromMerkle getB)
|
||||||
|
|
||||||
|
streamToListEither
|
||||||
|
:: (Monad m)
|
||||||
|
=> Stream (Of a) m (Either e ())
|
||||||
|
-> m (Either e [a])
|
||||||
|
streamToListEither = fmap runStreamOfA . S.toList
|
||||||
|
|
||||||
|
runStreamOfA :: (Functor m) => Of a (m ()) -> m a
|
||||||
|
runStreamOfA (a S.:> e) = a <$ e
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
streamMerkle1
|
streamMerkle1
|
||||||
|
|
|
@ -64,6 +64,7 @@ commands :: GitPerks m => Parser (GitCLI m ())
|
||||||
commands =
|
commands =
|
||||||
hsubparser ( command "export" (info pExport (progDesc "export repo to hbs2-git"))
|
hsubparser ( command "export" (info pExport (progDesc "export repo to hbs2-git"))
|
||||||
<> command "import" (info pImport (progDesc "import repo from reflog"))
|
<> command "import" (info pImport (progDesc "import repo from reflog"))
|
||||||
|
<> command "fsck" (info pFsck (progDesc "check objects from a last reflog transaction"))
|
||||||
<> command "key" (info pKey (progDesc "key management"))
|
<> command "key" (info pKey (progDesc "key management"))
|
||||||
<> command "manifest" (info pManifest (progDesc "manifest commands"))
|
<> command "manifest" (info pManifest (progDesc "manifest commands"))
|
||||||
<> command "track" (info pTrack (progDesc "track tools"))
|
<> command "track" (info pTrack (progDesc "track tools"))
|
||||||
|
@ -130,6 +131,13 @@ pImport = do
|
||||||
git <- Git.gitDir >>= orThrowUser "not a git dir"
|
git <- Git.gitDir >>= orThrowUser "not a git dir"
|
||||||
importRepoWait puk
|
importRepoWait puk
|
||||||
|
|
||||||
|
pFsck :: GitPerks m => Parser (GitCLI m ())
|
||||||
|
pFsck = do
|
||||||
|
lww <- argument pLwwKey (metavar "LWWREF")
|
||||||
|
pure do
|
||||||
|
git <- Git.gitDir >>= orThrowUser "not a git dir"
|
||||||
|
fsckRepo lww
|
||||||
|
|
||||||
pTools :: GitPerks m => Parser (GitCLI m ())
|
pTools :: GitPerks m => Parser (GitCLI m ())
|
||||||
pTools = hsubparser ( command "dump-pack" (info pDumpPack (progDesc "dump hbs2 git pack"))
|
pTools = hsubparser ( command "dump-pack" (info pDumpPack (progDesc "dump hbs2 git pack"))
|
||||||
<> command "show-ref" (info pShowRef (progDesc "show current references"))
|
<> command "show-ref" (info pShowRef (progDesc "show current references"))
|
||||||
|
@ -467,6 +475,12 @@ theDict = do
|
||||||
|
|
||||||
|
|
||||||
myEntries = do
|
myEntries = do
|
||||||
|
entry $ bindMatch "lww:fsck" $ nil_ $ \case
|
||||||
|
[StringLike puk] -> lift do
|
||||||
|
lww <- orThrowUser "bad lwwref key" (fromStringMay puk)
|
||||||
|
git <- Git.gitDir >>= orThrowUser "not a git dir"
|
||||||
|
fsckRepo lww
|
||||||
|
|
||||||
entry $ bindMatch "remote:hbs2:show" $ nil_ $ \case
|
entry $ bindMatch "remote:hbs2:show" $ nil_ $ \case
|
||||||
_ -> do
|
_ -> do
|
||||||
-- TODO: move-to-HBS2.Local.CLI
|
-- TODO: move-to-HBS2.Local.CLI
|
||||||
|
|
|
@ -11,8 +11,20 @@ import HBS2.Git.Data.Tx.Git
|
||||||
import HBS2.Git.Data.LWWBlock
|
import HBS2.Git.Data.LWWBlock
|
||||||
import HBS2.Git.Data.RepoHead
|
import HBS2.Git.Data.RepoHead
|
||||||
|
|
||||||
|
import HBS2.Data.Detect (readLogThrow)
|
||||||
|
import HBS2.Merkle.Walk
|
||||||
|
import HBS2.Peer.Proto.LWWRef
|
||||||
|
import HBS2.Storage
|
||||||
|
import HBS2.Storage.Operations.Missed
|
||||||
|
import HBS2.Storage.Operations.ByteString
|
||||||
|
-- import HBS2.Git.Data.GK
|
||||||
|
-- import HBS2.Git.Data.RepoHead
|
||||||
|
import HBS2.Storage.Operations.Class
|
||||||
|
|
||||||
import Data.ByteString.Lazy qualified as LBS
|
import Data.ByteString.Lazy qualified as LBS
|
||||||
|
|
||||||
|
import Control.Arrow ((>>>))
|
||||||
|
import Control.Concurrent (threadDelay)
|
||||||
import Data.HashMap.Strict (HashMap)
|
import Data.HashMap.Strict (HashMap)
|
||||||
import Data.HashMap.Strict qualified as HM
|
import Data.HashMap.Strict qualified as HM
|
||||||
import Text.InterpolatedString.Perl6 (qc)
|
import Text.InterpolatedString.Perl6 (qc)
|
||||||
|
@ -232,6 +244,142 @@ importRepoWait lwwKey = do
|
||||||
pause @'Seconds 2
|
pause @'Seconds 2
|
||||||
pure (IWaitLWWBlock 5)
|
pure (IWaitLWWBlock 5)
|
||||||
|
|
||||||
|
newtype CanNotReadLWWBlock = CanNotReadLWWBlock (LWWRefKey HBS2Basic)
|
||||||
|
deriving (Show) via (AsBase58 (LWWRefKey HBS2Basic))
|
||||||
|
instance Exception CanNotReadLWWBlock
|
||||||
|
|
||||||
|
newtype CanNotReadLWWHashRef = CanNotReadLWWHashRef (PubKey Sign HBS2Basic)
|
||||||
|
deriving (Show)
|
||||||
|
instance Exception CanNotReadLWWHashRef
|
||||||
|
|
||||||
|
newtype NoBlocksInMerkle = NoBlocksInMerkle HashRef
|
||||||
|
deriving (Show)
|
||||||
|
instance Exception NoBlocksInMerkle
|
||||||
|
|
||||||
|
newtype GetBlockError = GetBlockError HashRef
|
||||||
|
deriving (Show)
|
||||||
|
instance Exception GetBlockError
|
||||||
|
|
||||||
|
newtype GetOrFetchBlockError = GetOrFetchBlockError (Hash HbSync)
|
||||||
|
deriving (Show)
|
||||||
|
instance Exception GetOrFetchBlockError
|
||||||
|
|
||||||
|
newtype FsckError = FsckError Text
|
||||||
|
deriving (Show)
|
||||||
|
instance Exception FsckError
|
||||||
|
|
||||||
|
fsckRepo :: ( GitPerks m
|
||||||
|
, MonadReader GitEnv m
|
||||||
|
, HasAPI PeerAPI UNIX m
|
||||||
|
, HasAPI LWWRefAPI UNIX m
|
||||||
|
, HasAPI RefLogAPI UNIX m
|
||||||
|
)
|
||||||
|
=> LWWRefKey 'HBS2Basic
|
||||||
|
-> m ()
|
||||||
|
|
||||||
|
fsckRepo lwwKey = do
|
||||||
|
env <- ask
|
||||||
|
sto' <- asks _storage
|
||||||
|
peerAPI <- getAPI @PeerAPI @UNIX
|
||||||
|
|
||||||
|
let
|
||||||
|
getBF = getBlockOrFetch callBlockFetch (getBlock sto')
|
||||||
|
getBJ = fmap Just . getBF
|
||||||
|
|
||||||
|
let
|
||||||
|
getBJ' :: Hash HbSync -> IO (Maybe LBS.ByteString)
|
||||||
|
getBJ' = fmap Just . getBlockOrFetch (callBlockFetchIO peerAPI) (getBlock sto')
|
||||||
|
sto = AnyStorage (AdHocStorage @IO sto' getBJ')
|
||||||
|
|
||||||
|
(LWWRef{..}, LWWBlockData{..}) <- maybe (throwIO (CanNotReadLWWBlock lwwKey)) pure
|
||||||
|
=<< readLWWBlock sto lwwKey
|
||||||
|
|
||||||
|
hr <- maybe (throwIO (CanNotReadLWWHashRef lwwRefLogPubKey)) pure
|
||||||
|
=<< getRefLogMerkle lwwRefLogPubKey
|
||||||
|
liftIO . print $ "Reflog merkle hash:" <+> pretty hr
|
||||||
|
|
||||||
|
-- mapM_ (liftIO . print . pretty) =<< readLogThrow getBJ hr
|
||||||
|
|
||||||
|
-- readLogThrow getBJ hr >>= mapM_ \txh -> do
|
||||||
|
|
||||||
|
txh <- maybe (throwIO (NoBlocksInMerkle hr)) pure
|
||||||
|
=<< S.last_ do
|
||||||
|
(orThrowPassIO <=< streamMerkle @HashRef getBJ)
|
||||||
|
(fromHashRef hr)
|
||||||
|
do
|
||||||
|
|
||||||
|
liftIO . print $ "tx:" <+> pretty txh
|
||||||
|
|
||||||
|
txbs <- getBF (fromHashRef txh)
|
||||||
|
<&> deserialiseOrFail @(RefLogUpdate L4Proto)
|
||||||
|
>>= orThrow UnsupportedFormat
|
||||||
|
|
||||||
|
(n, rhh, blkh) <- unpackTx txbs
|
||||||
|
|
||||||
|
rh <- catFromMerkle
|
||||||
|
(fmap Just . getBF)
|
||||||
|
(fromHashRef rhh)
|
||||||
|
>>= orThrowPassIO
|
||||||
|
>>= (deserialiseOrFail @RepoHead >>> orThrow UnsupportedFormat)
|
||||||
|
|
||||||
|
findMissedBlocks2 sto blkh
|
||||||
|
& S.mapM_ (getBF . fromHashRef)
|
||||||
|
|
||||||
|
liftIO . print $ "All blocks fetched for tx" <+> pretty txh
|
||||||
|
|
||||||
|
-- Double check. Ensure readTx has everything needed
|
||||||
|
_ <- (orThrowPassIO <=< runExceptT) do
|
||||||
|
readTx sto txh
|
||||||
|
|
||||||
|
bundlesCount <- (orThrowPassIO . runStreamOfA <=< S.length) do
|
||||||
|
streamMerkle @HashRef getBJ (fromHashRef blkh)
|
||||||
|
& S.mapM (\bh -> bh <$ getBF (fromHashRef blkh))
|
||||||
|
& S.mapM (orThrowPassIO <=< runExceptT . readBundle sto rh)
|
||||||
|
|
||||||
|
liftIO . print $ "All bundles (" <+> pretty bundlesCount
|
||||||
|
<+> ") fetched and checked for tx" <+> pretty txh
|
||||||
|
|
||||||
|
where
|
||||||
|
callBlockFetch
|
||||||
|
:: ( MonadUnliftIO m
|
||||||
|
, HasAPI PeerAPI UNIX m
|
||||||
|
)
|
||||||
|
=> Hash HbSync -> m ()
|
||||||
|
callBlockFetch h = do
|
||||||
|
peerAPI <- getAPI @PeerAPI @UNIX
|
||||||
|
liftIO $ callBlockFetchIO peerAPI h
|
||||||
|
|
||||||
|
callBlockFetchIO :: ServiceCaller PeerAPI UNIX -> Hash HbSync -> IO ()
|
||||||
|
callBlockFetchIO peerAPI h = do
|
||||||
|
race (pause @'Seconds 1)
|
||||||
|
(callService @RpcFetch peerAPI (HashRef h))
|
||||||
|
>>= orThrow BlockFetchRequestTimeout
|
||||||
|
>>= orThrow BlockFetchRequestError
|
||||||
|
|
||||||
|
data BlockFetchRequestTimeout = BlockFetchRequestTimeout deriving (Show)
|
||||||
|
instance Exception BlockFetchRequestTimeout
|
||||||
|
|
||||||
|
data BlockFetchRequestError = BlockFetchRequestError deriving (Show)
|
||||||
|
instance Exception BlockFetchRequestError
|
||||||
|
|
||||||
|
getBlockOrFetch
|
||||||
|
:: (MonadIO m)
|
||||||
|
=> (Hash HbSync -> m ())
|
||||||
|
-> (Hash HbSync -> m (Maybe LBS.ByteString))
|
||||||
|
-> Hash HbSync -> m LBS.ByteString
|
||||||
|
getBlockOrFetch fetch getB h = do
|
||||||
|
getB h >>= flip maybe pure do
|
||||||
|
fetch h
|
||||||
|
liftIO . print $ "Fetch block:" <+> pretty h
|
||||||
|
flip fix 1 \go attempt -> do
|
||||||
|
liftIO $ threadDelay (attempt * 10^6)
|
||||||
|
getB h >>= flip maybe pure do
|
||||||
|
if attempt < numAttempts
|
||||||
|
then go (attempt + 1)
|
||||||
|
else throwIO (GetOrFetchBlockError h)
|
||||||
|
where
|
||||||
|
numAttempts = 12
|
||||||
|
|
||||||
scanRefLog :: (GitPerks m, MonadReader GitEnv m)
|
scanRefLog :: (GitPerks m, MonadReader GitEnv m)
|
||||||
=> RefLogId
|
=> RefLogId
|
||||||
-> HashRef
|
-> HashRef
|
||||||
|
|
|
@ -17,10 +17,10 @@ import Data.Maybe
|
||||||
import Data.Set qualified as Set
|
import Data.Set qualified as Set
|
||||||
|
|
||||||
data RepoHeadType = RepoHeadType1
|
data RepoHeadType = RepoHeadType1
|
||||||
deriving stock (Enum,Generic)
|
deriving stock (Enum,Generic,Show)
|
||||||
|
|
||||||
data RepoHeadExt = RepoHeadExt
|
data RepoHeadExt = RepoHeadExt
|
||||||
deriving stock Generic
|
deriving stock (Generic,Show)
|
||||||
|
|
||||||
data RepoHead =
|
data RepoHead =
|
||||||
RepoHeadSimple
|
RepoHeadSimple
|
||||||
|
@ -33,7 +33,7 @@ data RepoHead =
|
||||||
, repoHeadRefs' :: [(GitRef, GitHash)]
|
, repoHeadRefs' :: [(GitRef, GitHash)]
|
||||||
, _repoHeadExt :: [RepoHeadExt]
|
, _repoHeadExt :: [RepoHeadExt]
|
||||||
}
|
}
|
||||||
deriving stock (Generic)
|
deriving stock (Generic,Show)
|
||||||
|
|
||||||
makeLenses ''RepoHead
|
makeLenses ''RepoHead
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,11 @@ instance Pretty (AsBase58 (PubKey 'Sign s )) => Pretty (AsBase58 (LWWRefKey s))
|
||||||
instance Pretty (AsBase58 (PubKey 'Sign s )) => Pretty (LWWRefKey s) where
|
instance Pretty (AsBase58 (PubKey 'Sign s )) => Pretty (LWWRefKey s) where
|
||||||
pretty (LWWRefKey k) = pretty (AsBase58 k)
|
pretty (LWWRefKey k) = pretty (AsBase58 k)
|
||||||
|
|
||||||
|
instance Pretty (AsBase58 (PubKey 'Sign s )) => Show (AsBase58 (LWWRefKey s)) where
|
||||||
|
-- show = show . pretty
|
||||||
|
-- show (AsBase58 (LWWRefKey k)) = show $ pretty (AsBase58 k)
|
||||||
|
show (AsBase58 lww) = show $ pretty lww
|
||||||
|
|
||||||
|
|
||||||
instance Pretty (LWWRef e) where
|
instance Pretty (LWWRef e) where
|
||||||
pretty (LWWRef{..}) = parens ( "lwwref" <> line
|
pretty (LWWRef{..}) = parens ( "lwwref" <> line
|
||||||
|
|
Loading…
Reference in New Issue