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
7988import Data.Bits
8089import Data.Foldable
90+ import Data.Maybe
8191import Data.String
8292import Data.Pool qualified as Pool
8393import Data.Text qualified as T
@@ -100,6 +110,7 @@ import Pact.Types.Util (AsString(..))
100110
101111import Chainweb.Logger
102112import Chainweb.Pact.Backend.SQLite.DirectV2
113+ import Chainweb.PayloadProvider
103114
104115import Chainweb.Version
105116import 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+
512645data LocatedSQ3Error = LocatedSQ3Error ! CallStack ! SQ3. Error
513646instance Show LocatedSQ3Error where
514647 show (LocatedSQ3Error cs e) =
0 commit comments