Skip to content

Commit 62ea499

Browse files
committed
A task to evaluate patched CEK machine on the mainnet scripts.
1 parent 79e3b20 commit 62ea499

File tree

7 files changed

+86
-51
lines changed

7 files changed

+86
-51
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ locally. You can use this program as a basis for your own re-evaluation, where
3636
you can modify various parameters to suit your needs:
3737

3838
- The [Main module](plutus-script-evaluation/evaluate-scripts/Main.hs) of the `evaluate-scripts` executable.
39-
- The main workhorse, function `evaluateScripts` in the [Evaluation module](plutus-script-evaluation/evaluate-scripts/Evaluation.hs) does the boring parts (aggregating relevant script evaluation inputs, streaming the data from DB to a local computer, decoding CBOR) so that you can do the interesting part: fold over the script evaluations from the Mainnet accessing all of the original evaluation inputs, to re-interpret them accordingly to your task, maintaining local state (accumulator) if needed.
39+
- The main workhorse, function `evaluateScripts` in the [Evaluation module](plutus-script-evaluation/evaluate-scripts/Evaluation.hs) does the boring parts (aggregating relevant script evaluation inputs, streaming the data from DB to a local computer, decoding CBOR, forking worker threads) so that you can do the interesting part: traverse script evaluations from the Mainnet accessing all of the original evaluation inputs to re-interpret them accordingly to your task;
40+
41+
If a task requires maintaining local state (accumulator) during the evaluation, you can use the `accumulateScripts` function in the [Evaluation module](plutus-script-evaluation/evaluate-scripts/Evaluation.hs). This function is a more general version of `evaluateScripts` that allows you to maintain local state during the evaluation.

nix/shell.nix

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ cabalProject: {
7474
description = "Evaluate Plutus Scripts from mainnet";
7575
group = "general";
7676
exec = ''
77-
cabal run evaluate-scripts -- --database-conn-str "$DB_CONN_STRING"
77+
cabal run evaluate-scripts -- --start-block=0 --database-conn-str "$DB_CONN_STRING"
7878
'';
7979
};
8080
};

plutus-script-evaluation/evaluate-scripts/Main.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ main :: IO ()
1515
main = withUtf8 do
1616
hSetBuffering stdin LineBuffering
1717
hSetBuffering stdout LineBuffering
18-
Options{optsDatabaseConnStr} <- execParser parserInfo
18+
Options{optsDatabaseConnStr, startBlock} <- execParser parserInfo
1919
displaySqlError $
2020
bracket (PG.connectPostgreSQL optsDatabaseConnStr) PG.close \conn -> do
21-
_result <- evaluateScripts conn mempty onScriptEvaluationInput
21+
evaluateScripts conn startBlock onScriptEvaluationInput
2222
putStrLn "Done evaluating scripts"
2323

2424
displaySqlError :: IO () -> IO ()

plutus-script-evaluation/evaluate-scripts/Options.hs

+14-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@ module Options (
88
)
99
where
1010

11+
import Cardano.Slotting.Block (BlockNo (BlockNo))
1112
import Data.ByteString (ByteString)
1213
import Options.Applicative qualified as O
1314

14-
newtype Options = Options {optsDatabaseConnStr :: ByteString}
15+
data Options = Options
16+
{ optsDatabaseConnStr :: ByteString
17+
, startBlock :: BlockNo
18+
}
1519
deriving (Show)
1620

1721
options :: O.Parser Options
@@ -27,6 +31,15 @@ options = do
2731
\/docs/current/libpq-connect.html#LIBPQ-CONNSTRING"
2832
]
2933
)
34+
startBlock <-
35+
O.option
36+
(O.maybeReader (Just . BlockNo . read))
37+
( mconcat
38+
[ O.long "start-block"
39+
, O.metavar "BLOCK_NO"
40+
, O.help "Block number to start from"
41+
]
42+
)
3043
pure Options{..}
3144

3245
parserInfo :: O.ParserInfo Options

plutus-script-evaluation/lib/Database/Query.hs

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module Database.Query where
22

3+
import Cardano.Slotting.Block (BlockNo (..))
34
import Cardano.Slotting.Slot (SlotNo)
45
import Control.Monad.IO.Unlift (MonadUnliftIO, withRunInIO)
5-
import Data.Function ((&))
66
import Data.Profunctor.Product.Default (Default)
77
import Database.Orphans ()
88
import Database.PostgreSQL.Simple (Connection)
@@ -29,6 +29,7 @@ import Opaleye (
2929
(.==),
3030
(.>=),
3131
)
32+
import Opaleye.Internal.Column (SqlNum (pgFromInteger))
3233

3334
insertCostModelValues
3435
:: (Default ToFields CostModelValuesRecord CostModelValuesRecordFields)
@@ -98,13 +99,17 @@ selectSerialisedScriptsBatch conn count =
9899
withScriptEvaluationEvents
99100
:: (MonadUnliftIO m)
100101
=> Connection
102+
-> BlockNo
101103
-> a
102104
-> (a -> ScriptEvaluationRecord -> m a)
103105
-> m a
104-
withScriptEvaluationEvents conn a f = do
105-
let select =
106-
selectTable scriptEvaluations
107-
& orderBy (asc seBlockNo)
106+
withScriptEvaluationEvents conn blockNo a f = do
107+
let startBlock = pgFromInteger (fromIntegral (unBlockNo blockNo))
108+
select = orderBy (asc seBlockNo) do
109+
res@(MkScriptEvaluationRecord' _slot block _ _ _ _ _ _ _ _ _ _) <-
110+
selectTable scriptEvaluations
111+
where_ (block .>= startBlock)
112+
pure res
108113
withRunInIO \runInIO ->
109114
runSelectFold conn select a \accum record ->
110115
runInIO (f accum record)

plutus-script-evaluation/lib/Evaluate.hs

+52-39
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
module Evaluate where
22

3+
import Cardano.Slotting.Block (unBlockNo)
34
import Codec.Serialise (deserialise)
5+
import Control.Concurrent (getNumCapabilities)
46
import Control.Monad (when)
57
import Control.Monad.IO.Unlift (MonadUnliftIO)
68
import Control.Monad.Trans.Except (runExceptT)
79
import Control.Monad.Trans.Writer (WriterT (runWriterT))
810
import Data.ByteString qualified as BSL
911
import Data.ByteString.Short qualified as BSS
12+
import Data.Either (isRight)
1013
import Data.Text qualified as Text
1114
import Data.Text.IO qualified as TIO
1215
import Data.Time.Clock (getCurrentTime)
@@ -31,6 +34,8 @@ import PlutusLedgerApi.V1 qualified as V1
3134
import PlutusLedgerApi.V2 qualified as V2
3235
import PlutusLedgerApi.V3 qualified as V3
3336
import Text.PrettyBy qualified as Pretty
37+
import UnliftIO (IORef, atomicModifyIORef', liftIO, newIORef, readIORef)
38+
import UnliftIO.Concurrent (forkFinally, threadDelay)
3439

3540
data ScriptEvaluationInput = MkScriptEvaluationInput
3641
{ seiPlutusLedgerLanguage :: PlutusLedgerLanguage
@@ -63,20 +68,49 @@ renderScriptEvaluationInput MkScriptEvaluationInput{..} =
6368
in Pretty.display uplc
6469
)
6570

66-
evaluateScripts
71+
accumulateScripts
6772
:: (MonadFail m, MonadUnliftIO m)
6873
=> Postgres.Connection
6974
-- ^ Database connection
75+
-> BlockNo
76+
-- ^ Block number to start from
7077
-> a
7178
-- ^ Initial accumulator
7279
-> (ScriptEvaluationInput -> a -> m a)
7380
-- ^ Accumulation function
7481
-> m a
75-
evaluateScripts conn initialAccum accumulate =
76-
Db.withScriptEvaluationEvents conn initialAccum \accum record -> do
82+
accumulateScripts conn startBlock initialAccum accumulate =
83+
Db.withScriptEvaluationEvents conn startBlock initialAccum \accum record -> do
7784
scriptInput <- inputFromRecord record
7885
accumulate scriptInput accum
7986

87+
evaluateScripts
88+
:: forall m
89+
. (MonadFail m, MonadUnliftIO m)
90+
=> Postgres.Connection
91+
-- ^ Database connection
92+
-> BlockNo
93+
-- ^ Block number to start from
94+
-> (ScriptEvaluationInput -> m ())
95+
-- ^ Callback
96+
-> m ()
97+
evaluateScripts conn startBlock callback = do
98+
maxThreads <- liftIO getNumCapabilities
99+
threadCounter <- newIORef 0
100+
Db.withScriptEvaluationEvents conn startBlock () \_unit record -> do
101+
waitForAFreeThread maxThreads threadCounter
102+
atomicModifyIORef' threadCounter \n -> (n + 1, ())
103+
_threadId <- forkFinally (callback =<< inputFromRecord record) \_ ->
104+
atomicModifyIORef' threadCounter \n -> (n - 1, ())
105+
pure ()
106+
where
107+
waitForAFreeThread :: Int -> IORef Int -> m ()
108+
waitForAFreeThread maxThreads counter = do
109+
threadCount <- readIORef counter
110+
when (threadCount >= maxThreads) do
111+
threadDelay 60_000 -- wait for 60ms
112+
waitForAFreeThread maxThreads counter
113+
80114
inputFromRecord
81115
:: (MonadFail m)
82116
=> Db.ScriptEvaluationRecord
@@ -113,63 +147,42 @@ inputFromRecord MkScriptEvaluationRecord'{..} = do
113147
, seiBlock = seBlockNo
114148
}
115149

116-
onScriptEvaluationInput :: ScriptEvaluationInput -> () -> IO ()
117-
onScriptEvaluationInput input@MkScriptEvaluationInput{..} _accum = do
150+
onScriptEvaluationInput :: ScriptEvaluationInput -> IO ()
151+
onScriptEvaluationInput input@MkScriptEvaluationInput{..} = do
118152
let
119153
(_logOutput, evaluationResult) =
120154
evaluateScriptRestricting
121155
seiPlutusLedgerLanguage
122156
seiMajorProtocolVersion
123157
Quiet
124158
seiEvaluationContext
125-
(ExBudget maxBound maxBound) -- will check the budget separately
159+
seiExBudget
126160
seiScript
127161
seiData
128162

129-
let budgetExceeded (ExBudget cpu mem) =
130-
let ExBudget cpuPaidFor memPaidFor = seiExBudget
131-
in cpu > cpuPaidFor || mem > memPaidFor
132-
133-
let evaluationSuccess =
134-
either (const False) (not . budgetExceeded) evaluationResult
135-
136163
print seiBlock
137164

165+
let evaluationSuccess = isRight evaluationResult
166+
138167
when (evaluationSuccess /= seiEvaluationSuccess) do
139168
let msg =
140169
"Script evaluation result ("
141170
++ show evaluationSuccess
142171
++ ") does not match the recorded result ("
143172
++ show seiEvaluationSuccess
144173
++ ")"
174+
++ "\n\nEvaluation result:\n"
175+
++ show evaluationResult
176+
++ "\n\nScript evaluation inputs:\n"
177+
++ renderScriptEvaluationInput input
178+
++ "\n\n"
179+
putStr msg
145180

146181
nonce <- getCurrentTime
147-
let logFile = show seiBlock ++ "_" ++ show nonce ++ ".log"
148-
149-
putStrLn msg
182+
let logFile = show (unBlockNo seiBlock) ++ "_" ++ show nonce ++ ".log"
150183
putStrLn $ "Writing log to " ++ logFile
151-
152-
TIO.writeFile logFile $
153-
Text.pack msg
154-
<> "\n\nEvaluation result:\n"
155-
<> Text.pack (show evaluationResult)
156-
<> "\n\nScript evaluation inputs:\n"
157-
<> Text.pack (renderScriptEvaluationInput input)
184+
TIO.writeFile logFile (Text.pack msg)
158185

159186
case evaluationResult of
160-
Left err ->
161-
putStrLn $ "Script evaluation was not successful: " <> show err
162-
Right spentExBudget -> do
163-
if budgetExceeded spentExBudget
164-
then do
165-
putStrLn "Budget exceeded!"
166-
putStrLn $ "Paid for: " <> show seiExBudget
167-
putStrLn $ "Consumed: " <> show spentExBudget
168-
else
169-
if seiExBudget == spentExBudget
170-
then do
171-
putStrLn $ "Budget matches exactly: " <> show spentExBudget
172-
else do
173-
putStrLn "Budget is sufficient:"
174-
putStrLn $ "Paid for: " <> show seiExBudget
175-
putStrLn $ "Consumed: " <> show spentExBudget
187+
Right _spentExBudget -> pure ()
188+
Left err -> putStrLn $ "Script evaluation was not successful: " <> show err

plutus-script-evaluation/plutus-script-evaluation.cabal

+4-2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ library
112112
, text
113113
, time
114114
, transformers
115+
, unliftio
115116
, unliftio-core
116117
, vector
117118

@@ -158,9 +159,10 @@ executable evaluate-scripts
158159
import: lang, deps-exe
159160
hs-source-dirs: evaluate-scripts
160161
main-is: Main.hs
161-
ghc-options: -threaded -rtsopts
162+
ghc-options: -threaded -with-rtsopts=-N
162163
other-modules: Options
163164
build-depends:
164-
, cardano-api ^>=10.4
165+
, cardano-api ^>=10.4
166+
, cardano-slotting
165167
, text
166168
, unliftio-core

0 commit comments

Comments
 (0)