@@ -38,71 +38,92 @@ import qualified Ouroboros.Consensus.Storage.LedgerDB.V1.BackingStore.Impl.LMDB
3838import qualified Ouroboros.Consensus.Storage.LedgerDB.V1.DbChangelog as V1
3939import qualified Ouroboros.Consensus.Storage.LedgerDB.V1.Lock as V1
4040import qualified Ouroboros.Consensus.Storage.LedgerDB.V1.Snapshots as V1
41+ import Ouroboros.Consensus.Storage.LedgerDB.V2.Args
4142import qualified Ouroboros.Consensus.Storage.LedgerDB.V2.InMemory as InMemory
4243import qualified Ouroboros.Consensus.Storage.LedgerDB.V2.InMemory as V2
44+ import qualified Ouroboros.Consensus.Storage.LedgerDB.V2.LSM as LSM
4345import qualified Ouroboros.Consensus.Storage.LedgerDB.V2.LedgerSeq as V2
4446import Ouroboros.Consensus.Util.CRC
4547import Ouroboros.Consensus.Util.IOLike
48+ import qualified System.Directory as Directory
4649import System.FS.API
4750import System.FS.API.Lazy
4851import System.FS.CRC
4952import System.FS.IO
5053import System.FilePath (splitFileName )
54+ import System.IO (hFlush , stdout )
5155import System.IO.Temp
5256
5357data Format
54- = Legacy
55- | Mem
56- | LMDB
58+ = Legacy FilePath
59+ | Mem FilePath
60+ | LMDB FilePath
61+ | LSM FilePath FilePath
5762 deriving (Show , Read )
5863
5964data Config = Config
6065 { from :: Format
6166 -- ^ Which format the input snapshot is in
62- , inpath :: FilePath
63- -- ^ Path to the input snapshot
6467 , to :: Format
6568 -- ^ Which format the output snapshot must be in
66- , outpath :: FilePath
67- -- ^ Path to the output snapshot
6869 }
6970
7071getCommandLineConfig :: IO (Config , BlockType )
7172getCommandLineConfig =
7273 execParser $
7374 info
74- ((,) <$> parseConfig <*> blockTypeParser <**> helper)
75+ ((,) <$> ( Config <$> parseConfig In <*> parseConfig Out ) <*> blockTypeParser <**> helper)
7576 (fullDesc <> progDesc " Utility for converting snapshots to and from UTxO-HD" )
7677
77- parseConfig :: Parser Config
78- parseConfig =
79- Config
80- <$> argument
81- auto
82- ( mconcat
83- [ help " From format (Legacy, Mem or LMDB)"
84- , metavar " FORMAT-IN"
85- ]
86- )
87- <*> strArgument
88- ( mconcat
89- [ help " Input dir/file. Use relative paths like ./100007913"
90- , metavar " PATH-IN"
91- ]
92- )
93- <*> argument
94- auto
95- ( mconcat
96- [ help " To format (Legacy, Mem or LMDB)"
97- , metavar " FORMAT-OUT"
98- ]
99- )
100- <*> strArgument
101- ( mconcat
102- [ help " Output dir/file Use relative paths like ./100007913"
103- , metavar " PATH-OUT"
104- ]
105- )
78+ data InOut = In | Out
79+
80+ inoutForGroup :: InOut -> String
81+ inoutForGroup In = " Input arguments:"
82+ inoutForGroup Out = " Output arguments:"
83+
84+ inoutForHelp :: InOut -> String -> String
85+ inoutForHelp In = (" Input " ++ )
86+ inoutForHelp Out = (" Output " ++ )
87+
88+ inoutForCommand :: InOut -> String -> String
89+ inoutForCommand In = (++ " -in" )
90+ inoutForCommand Out = (++ " -out" )
91+
92+ parseConfig :: InOut -> Parser Format
93+ parseConfig io =
94+ ( Legacy
95+ <$> parserOptionGroup
96+ (inoutForGroup io)
97+ (parsePath (inoutForCommand io " legacy" ) (inoutForHelp io " snapshot file" ))
98+ )
99+ <|> ( Mem
100+ <$> parserOptionGroup
101+ (inoutForGroup io)
102+ (parsePath (inoutForCommand io " mem" ) (inoutForHelp io " snapshot dir" ))
103+ )
104+ <|> ( LMDB
105+ <$> parserOptionGroup
106+ (inoutForGroup io)
107+ (parsePath (inoutForCommand io " lmdb" ) (inoutForHelp io " snapshot dir" ))
108+ )
109+ <|> ( LSM
110+ <$> parserOptionGroup
111+ (inoutForGroup io)
112+ (parsePath (inoutForCommand io " lsm-snapshot" ) (inoutForHelp io " snapshot dir" ))
113+ <*> parserOptionGroup
114+ (inoutForGroup io)
115+ (parsePath (inoutForCommand io " lsm-database" ) (inoutForHelp io " LSM database" ))
116+ )
117+
118+ parsePath :: String -> String -> Parser FilePath
119+ parsePath optName strHelp =
120+ strOption
121+ ( mconcat
122+ [ long optName
123+ , help strHelp
124+ , metavar " PATH"
125+ ]
126+ )
106127
107128-- Helpers
108129
@@ -140,31 +161,6 @@ instance StandardHash blk => Show (Error blk) where
140161 <> err
141162 show (ReadSnapshotCRCError fp err) = " An error occurred while reading the snapshot checksum at " <> show fp <> " : \n\t " <> show err
142163
143- checkSnapshotFileStructure :: Format -> FsPath -> SomeHasFS IO -> ExceptT (Error blk ) IO ()
144- checkSnapshotFileStructure m p (SomeHasFS fs) = case m of
145- Legacy -> want (doesFileExist fs) p " is NOT a file"
146- Mem -> newFormatCheck " tvar"
147- LMDB -> newFormatCheck " data.mdb"
148- where
149- want :: (FsPath -> IO Bool ) -> FsPath -> String -> ExceptT (Error blk ) IO ()
150- want fileType path err = do
151- exists <- Trans. lift $ fileType path
152- Monad. unless exists $ throwError $ SnapshotFormatMismatch m err
153-
154- isDir = (doesDirectoryExist, [] , " is NOT a directory" )
155- hasTablesDir = (doesDirectoryExist, [" tables" ], " DOES NOT contain a \" tables\" directory" )
156- hasState = (doesFileExist, [" state" ], " DOES NOT contain a \" state\" file" )
157- hasTables tb = (doesFileExist, [" tables" , tb], " DOES NOT contain a \" tables/" <> tb <> " \" file" )
158-
159- newFormatCheck tb =
160- mapM_
161- (\ (doCheck, extra, err) -> want (doCheck fs) (p </> mkFsPath extra) err)
162- [ isDir
163- , hasTablesDir
164- , hasState
165- , hasTables tb
166- ]
167-
168164load ::
169165 forall blk .
170166 ( CanStowLedgerTables (LedgerState blk )
@@ -176,10 +172,9 @@ load ::
176172 CodecConfig blk ->
177173 FilePath ->
178174 ExceptT (Error blk ) IO (ExtLedgerState blk EmptyMK , LedgerTables (ExtLedgerState blk ) ValuesMK )
179- load config@ Config {inpath = pathToDiskSnapshot -> Just (fs @ ( SomeHasFS hasFS), path, ds)} rr ccfg tempFP =
175+ load config rr ccfg tempFP =
180176 case from config of
181- Legacy -> do
182- checkSnapshotFileStructure Legacy path fs
177+ Legacy (pathToDiskSnapshot -> Just (fs@ (SomeHasFS hasFS), path, _)) -> do
183178 (st, checksumAsRead) <-
184179 first unstowLedgerTables
185180 <$> withExceptT
@@ -196,13 +191,11 @@ load config@Config{inpath = pathToDiskSnapshot -> Just (fs@(SomeHasFS hasFS), pa
196191 SnapshotError $
197192 InitFailureRead ReadSnapshotDataCorruption
198193 pure (forgetLedgerTables st, projectLedgerTables st)
199- Mem -> do
200- checkSnapshotFileStructure Mem path fs
194+ Mem (pathToDiskSnapshot -> Just (fs, _, ds)) -> do
201195 (ls, _) <- withExceptT SnapshotError $ V2. loadSnapshot nullTracer rr ccfg fs ds
202196 let h = V2. currentHandle ls
203197 (V2. state h,) <$> Trans. lift (V2. readAll (V2. tables h) (V2. state h))
204- LMDB -> do
205- checkSnapshotFileStructure LMDB path fs
198+ LMDB (pathToDiskSnapshot -> Just (fs, _, ds)) -> do
206199 ((dbch, k, bstore), _) <-
207200 withExceptT SnapshotError $
208201 V1. loadSnapshot
@@ -215,7 +208,8 @@ load config@Config{inpath = pathToDiskSnapshot -> Just (fs@(SomeHasFS hasFS), pa
215208 values <- Trans. lift (V1. bsReadAll bstore (V1. changelogLastFlushedState dbch))
216209 _ <- Trans. lift $ RR. release k
217210 pure (V1. current dbch, values)
218- load _ _ _ _ = error " Malformed input path!"
211+ LSM _ _ -> error " unimplemented"
212+ _ -> error " Malformed input path!"
219213
220214store ::
221215 ( CanStowLedgerTables (LedgerState blk )
@@ -227,9 +221,9 @@ store ::
227221 (ExtLedgerState blk EmptyMK , LedgerTables (ExtLedgerState blk ) ValuesMK ) ->
228222 SomeHasFS IO ->
229223 IO ()
230- store config@ Config {outpath = pathToDiskSnapshot -> Just (fs @ ( SomeHasFS hasFS), path, DiskSnapshot _ suffix)} ccfg (state, tbs) tempFS =
224+ store config ccfg (state, tbs) tempFS =
231225 case to config of
232- Legacy -> do
226+ Legacy (p @ (pathToDiskSnapshot -> Just (fs @ ( SomeHasFS hasFS), path, _))) -> do
233227 crc <-
234228 writeExtLedgerState
235229 fs
@@ -238,11 +232,27 @@ store config@Config{outpath = pathToDiskSnapshot -> Just (fs@(SomeHasFS hasFS),
238232 (stowLedgerTables $ state `withLedgerTables` tbs)
239233 withFile hasFS (path <.> " checksum" ) (WriteMode MustBeNew ) $ \ h ->
240234 Monad. void $ hPutAll hasFS h . BS. toLazyByteString . BS. word32HexFixed $ getCRC crc
241- Mem -> do
235+ putStrLn " DONE"
236+ putStrLn $
237+ unlines $
238+ [ " You can now copy the file "
239+ ++ p
240+ ++ " to your `ledger` directory in your ChainDB storage."
241+ , " Note this snapshot can only be used by cardano-node <10.4."
242+ ]
243+ Mem (p@ (pathToDiskSnapshot -> Just (fs, _, DiskSnapshot _ suffix))) -> do
242244 lseq <- V2. empty state tbs $ V2. newInMemoryLedgerTablesHandle nullTracer fs
243245 let h = V2. currentHandle lseq
244246 Monad. void $ InMemory. implTakeSnapshot ccfg nullTracer fs suffix h
245- LMDB -> do
247+ putStrLn " DONE"
248+ putStrLn $
249+ unlines $
250+ [ " You can now copy the directory "
251+ ++ p
252+ ++ " to your `ledger` directory in your ChainDB storage."
253+ , " Note this snapshot can only be used by cardano-node >=10.4 configured to use the InMemory backend (set the \" LedgerDB\" .\" Backend\" key in your config file to \" V2InMemory\" or leave it undefined)."
254+ ]
255+ LMDB (p@ (pathToDiskSnapshot -> Just (fs, _, DiskSnapshot _ suffix))) -> do
246256 chlog <- newTVarIO (V1. empty state)
247257 lock <- V1. mkLedgerDBLock
248258 bs <-
@@ -254,7 +264,43 @@ store config@Config{outpath = pathToDiskSnapshot -> Just (fs@(SomeHasFS hasFS),
254264 (V1. InitFromValues (pointSlot $ getTip state) state tbs)
255265 Monad. void $ V1. withReadLock lock $ do
256266 V1. implTakeSnapshot chlog ccfg nullTracer (V1. SnapshotsFS fs) bs suffix
257- store _ _ _ _ = error " Malformed output path!"
267+ putStrLn " DONE"
268+ putStrLn $
269+ unlines $
270+ [ " You can now copy the directory "
271+ ++ p
272+ ++ " to your `ledger` directory in your ChainDB storage."
273+ , " Note this snapshot can only be used by cardano-node >=10.4 configured to use the LMDB backend (set the \" LedgerDB\" .\" Backend\" key in your config file to \" V1LMDB\" )."
274+ ]
275+ LSM (p@ (pathToDiskSnapshot -> Just (fs, _, DiskSnapshot _ suffix))) dbPath -> do
276+ exists <- Directory. doesDirectoryExist dbPath
277+ Monad. when (not exists) $ Directory. createDirectory dbPath
278+ RR. withRegistry $ \ reg -> do
279+ (_, SomeHasFSAndBlockIO hasFS blockIO) <- LSM. stdMkBlockIOFS dbPath reg
280+ salt <- LSM. stdGenSalt
281+ LSM. withNewSession nullTracer hasFS blockIO salt (mkFsPath [" " ]) $ \ session -> do
282+ lsmTable <- LSM. tableFromValuesMK reg session tbs
283+ lsmHandle <- LSM. newLSMLedgerTablesHandle nullTracer reg session lsmTable
284+ Monad. void $
285+ LSM. implTakeSnapshot
286+ ccfg
287+ nullTracer
288+ fs
289+ suffix
290+ (V2. StateRef state lsmHandle)
291+ putStrLn " DONE"
292+ putStrLn $
293+ unlines $
294+ [ " You can now:"
295+ , " - copy the directory "
296+ ++ p
297+ ++ " to your `ledger` directory in your ChainDB storage."
298+ , " - copy the directory "
299+ ++ dbPath
300+ ++ " to your fast storage device and point to it in your config file."
301+ , " Note this snapshot can only be used by cardano-node >=10.7 configured to use the LSM backend (set the \" LedgerDB\" .\" Backend\" key in your config file to \" V2LSM\" )."
302+ ]
303+ _ -> error " Malformed output path!"
258304
259305main :: IO ()
260306main = withStdTerminalHandles $ do
@@ -270,9 +316,10 @@ main = withStdTerminalHandles $ do
270316 withSystemTempDirectory " lmdb" $ \ dir -> do
271317 let tempFS = SomeHasFS $ ioHasFS $ MountPoint dir
272318 RR. withRegistry $ \ rr -> do
273- putStrLn " Loading snapshot..."
319+ putStr " Loading snapshot..."
320+ hFlush stdout
274321 state <- either throwIO pure =<< runExceptT (load conf rr ccfg dir)
275- putStrLn " Loaded snapshot"
276- putStrLn " Writing snapshot..."
322+ putStrLn " DONE"
323+ putStr " Writing snapshot..."
324+ hFlush stdout
277325 store conf ccfg state tempFS
278- putStrLn " Written snapshot"
0 commit comments