Skip to content

Commit 5a58c7e

Browse files
committed
Add lsm tool
1 parent 72b1505 commit 5a58c7e

4 files changed

Lines changed: 174 additions & 0 deletions

File tree

cardano-db-tool/app/cardano-db-tool.hs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import Cardano.Db
44
import Cardano.DbSync.Config.Types hiding (CmdVersion, LogFileDir)
55
import Cardano.DbTool
6+
import Cardano.DbTool.LsmDebug
67
import Cardano.Slotting.Slot (SlotNo (..))
78
import Control.Applicative (optional)
89
import Control.Monad (unless, void, when)
@@ -43,6 +44,7 @@ data Command
4344
| CmdPrepareSnapshot !PrepareSnapshotArgs
4445
| CmdValidateDb !TxOutVariantType
4546
| CmdValidateAddressBalance !LedgerValidationParams !TxOutVariantType
47+
| CmdLsmDebug !LsmDebugParams
4648
| CmdVersion
4749

4850
runCommand :: Command -> IO ()
@@ -67,6 +69,7 @@ runCommand cmd =
6769
CmdPrepareSnapshot pargs -> runPrepareSnapshot pargs
6870
CmdValidateDb txOutAddressType -> runDbValidation txOutAddressType
6971
CmdValidateAddressBalance params txOutAddressType -> runLedgerValidation params txOutAddressType
72+
CmdLsmDebug params -> runLsmDebug params
7073
CmdVersion -> runVersionCommand
7174

7275
runCreateMigration :: MigrationDir -> TxOutVariantType -> IO ()
@@ -153,6 +156,10 @@ pCommand =
153156
Opt.info
154157
(CmdValidateAddressBalance <$> pValidateLedgerParams <*> pTxOutVariantType)
155158
(Opt.progDesc "Run validation checks against the database and the ledger Utxo set.")
159+
, Opt.command "lsm-debug" $
160+
Opt.info
161+
pLsmDebug
162+
(Opt.progDesc "Look up a TxIn in an LSM snapshot and attempt to decode the TxOut.")
156163
, Opt.command "version" $
157164
Opt.info
158165
(pure CmdVersion)
@@ -264,6 +271,32 @@ pLedgerStateDir =
264271
<> Opt.metavar "FILEPATH"
265272
)
266273

274+
pLsmDebug :: Parser Command
275+
pLsmDebug =
276+
CmdLsmDebug <$>
277+
( LsmDebugParams
278+
<$> pConfigFile
279+
<*> pLedgerStateDir
280+
<*> Opt.strOption
281+
( Opt.long "tx-hash"
282+
<> Opt.help "Hex-encoded transaction hash"
283+
<> Opt.metavar "TXHASH"
284+
)
285+
<*> Opt.option Opt.auto
286+
( Opt.long "tx-index"
287+
<> Opt.help "Transaction output index"
288+
<> Opt.metavar "INDEX"
289+
<> Opt.value 0
290+
)
291+
<*> optional
292+
( Opt.strOption
293+
( Opt.long "snapshot"
294+
<> Opt.help "LSM snapshot name to query (default: derived from most recent .lstate file)"
295+
<> Opt.metavar "SNAPSHOT"
296+
)
297+
)
298+
)
299+
267300
pConfigFile :: Parser ConfigFile
268301
pConfigFile =
269302
ConfigFile

cardano-db-tool/cardano-db-tool.cabal

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ library
5252
Cardano.DbTool.Validate.TxAccounting
5353
Cardano.DbTool.Validate.Util
5454
Cardano.DbTool.Validate.Withdrawal
55+
Cardano.DbTool.LsmDebug
5556
Paths_cardano_db_tool
5657

5758
build-depends: base >= 4.14 && < 5
@@ -73,8 +74,14 @@ library
7374
, extra
7475
, ouroboros-consensus
7576
, ouroboros-consensus:cardano
77+
, ouroboros-consensus:lsm
7678
, ouroboros-network:api
79+
, lsm-tree
80+
, primitive
7781
, random
82+
, filepath
83+
, fs-api
84+
, vector
7885
, random-shuffle
7986
, text
8087
, text-icu

cardano-db-tool/src/Cardano/DbTool.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module Cardano.DbTool (
22
module X,
33
) where
44

5+
import Cardano.DbTool.LsmDebug as X
56
import Cardano.DbTool.PrepareSnapshot as X
67
import Cardano.DbTool.Report as X
78
import Cardano.DbTool.UtxoSet as X
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
module Cardano.DbTool.LsmDebug
2+
( LsmDebugParams (..)
3+
, runLsmDebug
4+
) where
5+
6+
import Cardano.DbSync.Config
7+
import Cardano.DbSync.Config.Cardano
8+
import Cardano.DbSync.Config.Types (LedgerBackend (..), LedgerStateDir (..))
9+
import Cardano.DbSync.Error
10+
import Cardano.DbSync.Ledger.State (listLedgerStateFilesOrdered, loadLedgerStateFromFile,
11+
mkHandleFromValues)
12+
import Cardano.DbSync.Ledger.Types (CardanoLedgerState (..), LedgerStateFile (..))
13+
14+
import Cardano.Crypto.Hash.Class (hashFromTextAsHex)
15+
import Cardano.Ledger.BaseTypes (TxIx (..))
16+
import Cardano.Ledger.Crypto (StandardCrypto)
17+
import Cardano.Ledger.SafeHash (unsafeMakeSafeHash)
18+
import Cardano.Ledger.TxIn (TxId (..), TxIn (..))
19+
20+
import Control.Exception (SomeException, evaluate, try)
21+
import Control.Monad.Trans.Except (runExceptT)
22+
import Control.ResourceRegistry (withRegistry)
23+
import Control.Tracer (nullTracer)
24+
25+
import Data.ByteString (ByteString)
26+
import qualified Data.ByteString as BS
27+
import Data.Proxy (Proxy (..))
28+
import Data.String (fromString)
29+
import Data.Text (Text)
30+
import qualified Data.Text as Text
31+
import qualified Data.Vector as V
32+
import Data.Word (Word16)
33+
import qualified Database.LSMTree as LSM
34+
import Numeric (showHex)
35+
import qualified Data.Primitive.ByteArray as PBA
36+
import qualified Data.Vector.Primitive as VP
37+
38+
import Ouroboros.Consensus.Cardano.Block (CardanoBlock)
39+
import Ouroboros.Consensus.Ledger.Extended (ExtLedgerState)
40+
import Ouroboros.Consensus.Ledger.Tables.Utils (emptyLedgerTables)
41+
import Ouroboros.Consensus.Storage.LedgerDB.V2.LSM
42+
(SomeHasFSAndBlockIO (..), TxInBytes (..), TxOutBytes (..),
43+
fromTxOutBytes, stdMkBlockIOFS, toTxInBytes)
44+
45+
import System.FilePath (dropExtension, takeFileName, (</>))
46+
import System.FS.API (mkFsPath)
47+
48+
import Prelude
49+
50+
data LsmDebugParams = LsmDebugParams
51+
{ ldpConfigFile :: !ConfigFile
52+
, ldpLedgerStateDir :: !LedgerStateDir
53+
, ldpTxHash :: !Text
54+
, ldpTxIndex :: !Word16
55+
, ldpSnapshot :: !(Maybe Text)
56+
}
57+
58+
runLsmDebug :: LsmDebugParams -> IO ()
59+
runLsmDebug params = do
60+
enc <- readSyncNodeConfig (ldpConfigFile params)
61+
genCfg <- runOrThrowIO $ runExceptT $ readCardanoGenesisConfig enc
62+
let cfg = mkTopLevelConfig genCfg
63+
lsmDir = unLedgerStateDir (ldpLedgerStateDir params) </> "lsm"
64+
ledgerFiles <- listLedgerStateFilesOrdered (ldpLedgerStateDir params)
65+
case ledgerFiles of
66+
[] -> putStrLn "No ledger state files found"
67+
(lf : _) -> do
68+
putStrLn $ "Using ledger state file: " <> lsfFilePath lf
69+
-- Load era context from the state file (empty InMemory handle for era context only)
70+
eState <- loadLedgerStateFromFile
71+
(\_ _ -> mkHandleFromValues emptyLedgerTables)
72+
Nothing
73+
(LedgerBackendLSM Nothing)
74+
nullTracer cfg False
75+
(lsfOriginPoint lf)
76+
lf
77+
case eState of
78+
Left err -> putStrLn $ "Failed to load ledger state: " <> Text.unpack err
79+
Right cls -> do
80+
let snapshotName = case ldpSnapshot params of
81+
Just s -> Text.unpack s
82+
Nothing -> dropExtension $ takeFileName $ lsfFilePath lf
83+
lookupEntry lsmDir snapshotName (clsState cls) params
84+
85+
lookupEntry
86+
:: FilePath
87+
-> String
88+
-> ExtLedgerState CardanoBlock EmptyMK
89+
-> LsmDebugParams
90+
-> IO ()
91+
lookupEntry lsmDir snapshotName st params = do
92+
txIn <- parseTxIn (ldpTxHash params) (ldpTxIndex params)
93+
let key = toTxInBytes (Proxy @(ExtLedgerState CardanoBlock)) txIn
94+
putStrLn $ "Looking up TxIn in snapshot: " <> snapshotName
95+
withRegistry $ \reg -> do
96+
(_, SomeHasFSAndBlockIO hasFS blockIO) <- stdMkBlockIOFS lsmDir reg
97+
(_, session) <- allocate reg
98+
(\_ -> LSM.openSession nullTracer hasFS blockIO 0 (mkFsPath []))
99+
LSM.closeSession
100+
(_, table) <- allocate reg
101+
(\_ -> LSM.openTableFromSnapshot
102+
session
103+
(fromString snapshotName)
104+
(LSM.SnapshotLabel "UTxO table"))
105+
LSM.closeTable
106+
results <- LSM.lookups table (V.singleton key)
107+
case V.head results of
108+
LSM.NotFound ->
109+
putStrLn "TxIn not found in this snapshot"
110+
LSM.Found txOutBytes -> do
111+
putStrLn $ "Found! Raw bytes (hex): " <> toHex (toBS txOutBytes)
112+
result <- try (evaluate (fromTxOutBytes st key txOutBytes))
113+
:: IO (Either SomeException (TxOut (ExtLedgerState CardanoBlock)))
114+
case result of
115+
Left err -> putStrLn $ "Decode FAILED: " <> show err
116+
Right _ -> putStrLn "Decode OK"
117+
LSM.FoundWithBlob{} ->
118+
putStrLn "Unexpected blob entry"
119+
120+
parseTxIn :: Text -> Word16 -> IO (TxIn StandardCrypto)
121+
parseTxIn hashHex idx =
122+
case hashFromTextAsHex (Text.unpack hashHex) of
123+
Nothing -> fail $ "Invalid tx hash: " <> Text.unpack hashHex
124+
Just h -> pure $ TxIn (TxId (unsafeMakeSafeHash h)) (TxIx (fromIntegral idx))
125+
126+
toBS :: TxOutBytes -> ByteString
127+
toBS (TxOutBytes (LSM.RawBytes (VP.Vector off len barr))) =
128+
BS.pack [PBA.indexByteArray barr (off + i) | i <- [0 .. len - 1]]
129+
130+
toHex :: ByteString -> String
131+
toHex = BS.foldr' (\b acc -> pad (showHex b "") <> acc) ""
132+
where
133+
pad s = replicate (2 - length s) '0' <> s

0 commit comments

Comments
 (0)