Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.

Commit ff5633e

Browse files
committed
wip: BlockHistory changes
1. BlockHistoryMigration starts rewinding to the final block in BlockHistory and setting the consensus state to that block, as part of cleanup. (needs tests) 2. Many utilities moved from ChainwebPactDb to Backend.Utils. (these are not specific to PactDb though they are related to storage) 3. Stop maintaining a "latest" in ConsensusState table. This is only the latest row in BlockHistory table now. Change-Id: Id00000001c55cdeb49e0280cadaf17bb040a381f
1 parent 4e2d8cf commit ff5633e

File tree

8 files changed

+221
-193
lines changed

8 files changed

+221
-193
lines changed

src/Chainweb/Pact/Backend/ChainwebPactDb.hs

Lines changed: 0 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -761,58 +761,6 @@ createVersionedTable tablename db = do
761761
indexcreationstmt =
762762
"CREATE INDEX IF NOT EXISTS " <> tbl ixName <> " ON " <> tbl tablename <> "(txid DESC);"
763763

764-
setConsensusState :: SQ3.Database -> ConsensusState -> ExceptT LocatedSQ3Error IO ()
765-
setConsensusState db cs = do
766-
withSavepoint db SetConsensusSavePoint $ do
767-
exec' db
768-
"INSERT INTO ConsensusState (blockheight, hash, payloadhash, safety) VALUES \
769-
\(?, ?, ?, ?);"
770-
(toRow "final" $ _consensusStateFinal cs)
771-
exec' db
772-
"INSERT INTO ConsensusState (blockheight, hash, payloadhash, safety) VALUES \
773-
\(?, ?, ?, ?);"
774-
(toRow "safe" $ _consensusStateSafe cs)
775-
exec' db
776-
"INSERT INTO ConsensusState (blockheight, hash, payloadhash, safety) VALUES \
777-
\(?, ?, ?, ?);"
778-
(toRow "latest" $ _consensusStateLatest cs)
779-
where
780-
toRow safety SyncState {..} =
781-
[ SInt $ fromIntegral @BlockHeight @Int64 _syncStateHeight
782-
, SBlob $ runPutS (encodeBlockHash _syncStateBlockHash)
783-
, SBlob $ runPutS (encodeBlockPayloadHash _syncStateBlockPayloadHash)
784-
, SText safety
785-
]
786-
787-
getConsensusState :: SQ3.Database -> ExceptT LocatedSQ3Error IO (Maybe ConsensusState)
788-
getConsensusState db = do
789-
maybeState <- qry db "SELECT blockheight, hash, payloadhash, safety FROM ConsensusState ORDER BY safety ASC;"
790-
[] [RInt, RBlob, RBlob, RText] >>= \case
791-
[final, latest, safe] -> return $ Just ConsensusState
792-
{ _consensusStateFinal = readRow "final" final
793-
, _consensusStateLatest = readRow "latest" latest
794-
, _consensusStateSafe = readRow "safe" safe
795-
}
796-
[] -> return Nothing
797-
inv -> error $ "invalid contents of the ConsensusState table: " <> sshow inv
798-
case maybeState of
799-
Nothing -> do
800-
getLatestBlock db >>= \case
801-
Nothing -> return Nothing
802-
Just latest ->
803-
return $ Just $ ConsensusState latest latest latest
804-
Just s -> return (Just s)
805-
where
806-
readRow expectedType [SInt height, SBlob hash, SBlob payloadHash, SText type']
807-
| expectedType == type' = SyncState
808-
{ _syncStateHeight = fromIntegral @Int64 @BlockHeight height
809-
, _syncStateBlockHash = either error id $ runGetEitherS decodeBlockHash hash
810-
, _syncStateBlockPayloadHash = either error id $ runGetEitherS decodeBlockPayloadHash payloadHash
811-
}
812-
| otherwise = error $ "wrong type; expected " <> sshow expectedType <> " but got " <> sshow type'
813-
readRow expectedType invalidRow
814-
= error $ "invalid row: expected " <> sshow expectedType <> " but got row " <> sshow invalidRow
815-
816764
-- | Create all tables that exist pre-genesis
817765
-- TODO: migrate this logic to the checkpointer itself?
818766
initSchema :: SQLiteEnv -> IO ()
@@ -888,81 +836,3 @@ getSerialiser = do
888836
cid <- view blockHandlerChainId
889837
blockHeight <- view blockHandlerBlockHeight
890838
return $ pact5Serialiser cid blockHeight
891-
892-
getPayloadsAfter :: HasCallStack => SQLiteEnv -> Parent BlockHeight -> ExceptT LocatedSQ3Error IO [Ranked BlockPayloadHash]
893-
getPayloadsAfter db parentHeight = do
894-
qry db "SELECT blockheight, payloadhash FROM BlockHistory2 WHERE blockheight > ?"
895-
[SInt (fromIntegral @BlockHeight @Int64 (unwrapParent parentHeight))]
896-
[RInt, RBlob] >>= traverse
897-
\case
898-
[SInt bh, SBlob bhash] ->
899-
return $! Ranked (fromIntegral @Int64 @BlockHeight bh) $ either error id $ runGetEitherS decodeBlockPayloadHash bhash
900-
_ -> error "incorrect column type"
901-
902-
-- | Get the checkpointer's idea of the earliest block. The block height
903-
-- is the height of the block of the block hash.
904-
getEarliestBlock :: HasCallStack => SQLiteEnv -> ExceptT LocatedSQ3Error IO (Maybe RankedBlockHash)
905-
getEarliestBlock db = do
906-
r <- qry db qtext [] [RInt, RBlob] >>= mapM go
907-
case r of
908-
[] -> return Nothing
909-
(!o:_) -> return (Just o)
910-
where
911-
qtext = "SELECT blockheight, hash FROM BlockHistory2 ORDER BY blockheight ASC LIMIT 1"
912-
913-
go [SInt hgt, SBlob blob] =
914-
let hash = either error id $ runGetEitherS decodeBlockHash blob
915-
in return (RankedBlockHash (fromIntegral hgt) hash)
916-
go _ = fail "Chainweb.Pact.Backend.RelationalCheckpointer.doGetEarliest: impossible. This is a bug in chainweb-node."
917-
918-
-- | Get the checkpointer's idea of the latest block.
919-
getLatestBlock :: HasCallStack => SQLiteEnv -> ExceptT LocatedSQ3Error IO (Maybe SyncState)
920-
getLatestBlock db = do
921-
r <- qry db qtext [] [RInt, RBlob, RBlob] >>= mapM go
922-
case r of
923-
[] -> return Nothing
924-
(!o:_) -> return (Just o)
925-
where
926-
qtext = "SELECT blockheight, hash, payloadhash FROM BlockHistory2 ORDER BY blockheight DESC LIMIT 1"
927-
928-
go [SInt hgt, SBlob blob, SBlob pBlob] =
929-
let hash = either error id $ runGetEitherS decodeBlockHash blob
930-
in let pHash = either error id $ runGetEitherS decodeBlockPayloadHash pBlob
931-
in return $ SyncState
932-
{ _syncStateBlockHash = hash
933-
, _syncStateBlockPayloadHash = pHash
934-
, _syncStateHeight = int hgt
935-
}
936-
go r = fail $
937-
"Chainweb.Pact.Backend.ChainwebPactDb.getLatestBlock: impossible. This is a bug in chainweb-node. Details: "
938-
<> sshow r
939-
940-
lookupBlockWithHeight :: HasCallStack => SQ3.Database -> BlockHeight -> ExceptT LocatedSQ3Error IO (Maybe (Ranked BlockHash))
941-
lookupBlockWithHeight db bheight = do
942-
qry db qtext [SInt $ fromIntegral bheight] [RBlob] >>= \case
943-
[[SBlob hash]] -> return $! Just $!
944-
Ranked bheight (either error id $ runGetEitherS decodeBlockHash hash)
945-
[] -> return Nothing
946-
res -> error $ "Invalid result, " <> sshow res
947-
where
948-
qtext = "SELECT hash FROM BlockHistory2 WHERE blockheight = ?;"
949-
950-
lookupBlockHash :: HasCallStack => SQ3.Database -> BlockHash -> ExceptT LocatedSQ3Error IO (Maybe BlockHeight)
951-
lookupBlockHash db hash = do
952-
qry db qtext [SBlob (runPutS (encodeBlockHash hash))] [RInt] >>= \case
953-
[[SInt n]] -> return $! Just $! int n
954-
[] -> return $ Nothing
955-
res -> error $ "Invalid result, " <> sshow res
956-
where
957-
qtext = "SELECT blockheight FROM BlockHistory2 WHERE hash = ?;"
958-
959-
lookupRankedBlockHash :: HasCallStack => SQ3.Database -> RankedBlockHash -> IO Bool
960-
lookupRankedBlockHash db rankedBHash = throwOnDbError $ do
961-
qry db qtext
962-
[ SInt $ fromIntegral (_rankedBlockHashHeight rankedBHash)
963-
, SBlob $ runPutS $ encodeBlockHash $ _rankedBlockHashHash rankedBHash
964-
] [RInt] >>= \case
965-
[[SInt n]] -> return $! n == 1
966-
res -> error $ "Invalid result, " <> sshow res
967-
where
968-
qtext = "SELECT COUNT(*) FROM BlockHistory2 WHERE blockheight = ? AND hash = ?;"

src/Chainweb/Pact/Backend/Utils.hs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
{-# LANGUAGE ScopedTypeVariables #-}
88
{-# LANGUAGE TypeApplications #-}
99
{-# LANGUAGE GADTs #-}
10+
{-# LANGUAGE BlockArguments #-}
1011

1112

1213
{-# OPTIONS_GHC -fno-warn-orphans #-}
@@ -37,6 +38,14 @@ module Chainweb.Pact.Backend.Utils
3738
, rewindDbToBlock
3839
, rewindDbToGenesis
3940
, getEndTxId
41+
, setConsensusState
42+
, getConsensusState
43+
, getPayloadsAfter
44+
, getLatestBlock
45+
, getEarliestBlock
46+
, lookupBlockWithHeight
47+
, lookupBlockHash
48+
, lookupRankedBlockHash
4049
-- * Savepoints
4150
, withSavepoint
4251
, SavepointName(..)
@@ -78,6 +87,7 @@ import Control.Monad.Trans.Resource (ResourceT, allocate)
7887

7988
import Data.Bits
8089
import Data.Foldable
90+
import Data.Maybe
8191
import Data.String
8292
import Data.Pool qualified as Pool
8393
import Data.Text qualified as T
@@ -100,6 +110,7 @@ import Pact.Types.Util (AsString(..))
100110

101111
import Chainweb.Logger
102112
import Chainweb.Pact.Backend.SQLite.DirectV2
113+
import Chainweb.PayloadProvider
103114

104115
import Chainweb.Version
105116
import Chainweb.Utils
@@ -509,6 +520,128 @@ rewindDbToBlock db bh endingTxId = throwOnDbError $ do
509520
exec' db "DELETE FROM TransactionIndex WHERE blockheight > ?;"
510521
[ SInt (fromIntegral bh) ]
511522

523+
-- | Set the consensus state. Note that the "latest" parameter is ignored; the
524+
-- latest block is always the highest block in the BlockHistory table.
525+
setConsensusState :: SQ3.Database -> ConsensusState -> ExceptT LocatedSQ3Error IO ()
526+
setConsensusState db cs = do
527+
withSavepoint db SetConsensusSavePoint $ do
528+
exec' db
529+
"INSERT INTO ConsensusState (blockheight, hash, payloadhash, safety) VALUES \
530+
\(?, ?, ?, ?);"
531+
(toRow "final" $ _consensusStateFinal cs)
532+
exec' db
533+
"INSERT INTO ConsensusState (blockheight, hash, payloadhash, safety) VALUES \
534+
\(?, ?, ?, ?);"
535+
(toRow "safe" $ _consensusStateSafe cs)
536+
where
537+
toRow safety SyncState {..} =
538+
[ SInt $ fromIntegral @BlockHeight @Int64 _syncStateHeight
539+
, SBlob $ runPutS (encodeBlockHash _syncStateBlockHash)
540+
, SBlob $ runPutS (encodeBlockPayloadHash _syncStateBlockPayloadHash)
541+
, SText safety
542+
]
543+
544+
-- | Retrieve the latest "consensus state" including latest, safe, and final blocks.
545+
getConsensusState :: SQ3.Database -> ExceptT LocatedSQ3Error IO ConsensusState
546+
getConsensusState db = do
547+
latestBlock <- fromMaybe (error "before genesis") <$> getLatestBlock db
548+
qry db "SELECT blockheight, hash, payloadhash, safety FROM ConsensusState ORDER BY safety ASC;"
549+
[] [RInt, RBlob, RBlob, RText] >>= \case
550+
[final, safe] -> return $ ConsensusState
551+
{ _consensusStateFinal = readRow "final" final
552+
, _consensusStateSafe = readRow "safe" safe
553+
, _consensusStateLatest = latestBlock
554+
}
555+
inv -> error $ "invalid contents of the ConsensusState table: " <> sshow inv
556+
where
557+
readRow expectedType [SInt height, SBlob hash, SBlob payloadHash, SText type']
558+
| expectedType == type' = SyncState
559+
{ _syncStateHeight = fromIntegral @Int64 @BlockHeight height
560+
, _syncStateBlockHash = either error id $ runGetEitherS decodeBlockHash hash
561+
, _syncStateBlockPayloadHash = either error id $ runGetEitherS decodeBlockPayloadHash payloadHash
562+
}
563+
| otherwise = error $ "wrong type; expected " <> sshow expectedType <> " but got " <> sshow type'
564+
readRow expectedType invalidRow
565+
= error $ "invalid row: expected " <> sshow expectedType <> " but got row " <> sshow invalidRow
566+
567+
getPayloadsAfter :: HasCallStack => SQLiteEnv -> Parent BlockHeight -> ExceptT LocatedSQ3Error IO [Ranked BlockPayloadHash]
568+
getPayloadsAfter db parentHeight = do
569+
qry db "SELECT blockheight, payloadhash FROM BlockHistory2 WHERE blockheight > ?"
570+
[SInt (fromIntegral @BlockHeight @Int64 (unwrapParent parentHeight))]
571+
[RInt, RBlob] >>= traverse
572+
\case
573+
[SInt bh, SBlob bhash] ->
574+
return $! Ranked (fromIntegral @Int64 @BlockHeight bh) $ either error id $ runGetEitherS decodeBlockPayloadHash bhash
575+
_ -> error "incorrect column type"
576+
577+
-- | Get the checkpointer's idea of the earliest block. The block height
578+
-- is the height of the block of the block hash.
579+
getEarliestBlock :: HasCallStack => SQLiteEnv -> ExceptT LocatedSQ3Error IO (Maybe RankedBlockHash)
580+
getEarliestBlock db = do
581+
r <- qry db qtext [] [RInt, RBlob] >>= mapM go
582+
case r of
583+
[] -> return Nothing
584+
(!o:_) -> return (Just o)
585+
where
586+
qtext = "SELECT blockheight, hash FROM BlockHistory2 ORDER BY blockheight ASC LIMIT 1"
587+
588+
go [SInt hgt, SBlob blob] =
589+
let hash = either error id $ runGetEitherS decodeBlockHash blob
590+
in return (RankedBlockHash (fromIntegral hgt) hash)
591+
go _ = fail "Chainweb.Pact.Backend.RelationalCheckpointer.doGetEarliest: impossible. This is a bug in chainweb-node."
592+
593+
-- | Get the checkpointer's idea of the latest block.
594+
getLatestBlock :: HasCallStack => SQLiteEnv -> ExceptT LocatedSQ3Error IO (Maybe SyncState)
595+
getLatestBlock db = do
596+
r <- qry db qtext [] [RInt, RBlob, RBlob] >>= mapM go
597+
case r of
598+
[] -> return Nothing
599+
(!o:_) -> return (Just o)
600+
where
601+
qtext = "SELECT blockheight, hash, payloadhash FROM BlockHistory2 ORDER BY blockheight DESC LIMIT 1"
602+
603+
go [SInt hgt, SBlob blob, SBlob pBlob] =
604+
let hash = either error id $ runGetEitherS decodeBlockHash blob
605+
in let pHash = either error id $ runGetEitherS decodeBlockPayloadHash pBlob
606+
in return $ SyncState
607+
{ _syncStateBlockHash = hash
608+
, _syncStateBlockPayloadHash = pHash
609+
, _syncStateHeight = int hgt
610+
}
611+
go r = fail $
612+
"Chainweb.Pact.Backend.ChainwebPactDb.getLatestBlock: impossible. This is a bug in chainweb-node. Details: "
613+
<> sshow r
614+
615+
lookupBlockWithHeight :: HasCallStack => SQ3.Database -> BlockHeight -> ExceptT LocatedSQ3Error IO (Maybe (Ranked BlockHash))
616+
lookupBlockWithHeight db bheight = do
617+
qry db qtext [SInt $ fromIntegral bheight] [RBlob] >>= \case
618+
[[SBlob hash]] -> return $! Just $!
619+
Ranked bheight (either error id $ runGetEitherS decodeBlockHash hash)
620+
[] -> return Nothing
621+
res -> error $ "Invalid result, " <> sshow res
622+
where
623+
qtext = "SELECT hash FROM BlockHistory2 WHERE blockheight = ?;"
624+
625+
lookupBlockHash :: HasCallStack => SQ3.Database -> BlockHash -> ExceptT LocatedSQ3Error IO (Maybe BlockHeight)
626+
lookupBlockHash db hash = do
627+
qry db qtext [SBlob (runPutS (encodeBlockHash hash))] [RInt] >>= \case
628+
[[SInt n]] -> return $! Just $! int n
629+
[] -> return $ Nothing
630+
res -> error $ "Invalid result, " <> sshow res
631+
where
632+
qtext = "SELECT blockheight FROM BlockHistory2 WHERE hash = ?;"
633+
634+
lookupRankedBlockHash :: HasCallStack => SQ3.Database -> RankedBlockHash -> IO Bool
635+
lookupRankedBlockHash db rankedBHash = throwOnDbError $ do
636+
qry db qtext
637+
[ SInt $ fromIntegral (_rankedBlockHashHeight rankedBHash)
638+
, SBlob $ runPutS $ encodeBlockHash $ _rankedBlockHashHash rankedBHash
639+
] [RInt] >>= \case
640+
[[SInt n]] -> return $! n == 1
641+
res -> error $ "Invalid result, " <> sshow res
642+
where
643+
qtext = "SELECT COUNT(*) FROM BlockHistory2 WHERE blockheight = ? AND hash = ?;"
644+
512645
data LocatedSQ3Error = LocatedSQ3Error !CallStack !SQ3.Error
513646
instance Show LocatedSQ3Error where
514647
show (LocatedSQ3Error cs e) =

src/Chainweb/Pact/PactService.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ initialPayloadState
184184
-> IO ()
185185
initialPayloadState logger serviceEnv = do
186186
withSavepoint (_psReadWriteSql serviceEnv) RunGenesisSavePoint $ do
187-
latestBlock <- fmap _consensusStateLatest <$> Checkpointer.getConsensusState rwSql
187+
latestBlock <- Checkpointer.getLatestBlock rwSql
188188
case latestBlock of
189189
Nothing -> runGenesis
190190
Just ss
@@ -497,7 +497,7 @@ syncToFork
497497
-> IO ConsensusState
498498
syncToFork logger serviceEnv hints forkInfo = do
499499
(rewoundTxs, validatedTxs, newConsensusState) <- withSavepoint sql ValidateBlockSavePoint $ do
500-
pactConsensusState <- fromJuste <$> Checkpointer.getConsensusState sql
500+
pactConsensusState <- Checkpointer.getConsensusState sql
501501
let atTarget =
502502
_syncStateBlockHash (_consensusStateLatest pactConsensusState) ==
503503
_latestBlockHash (forkInfo._forkInfoTargetState)

0 commit comments

Comments
 (0)