Skip to content
This repository was archived by the owner on Dec 2, 2024. It is now read-only.

Commit 2d6f8b6

Browse files
Add evalEmulatorTrace
1 parent 7e699e3 commit 2d6f8b6

File tree

4 files changed

+108
-47
lines changed

4 files changed

+108
-47
lines changed

plutus-contract/src/Plutus/Trace/Emulator.hs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ module Plutus.Trace.Emulator(
6262
, runEmulatorStream
6363
, TraceConfig(..)
6464
, runEmulatorTrace
65+
, runEmulatorTrace'
66+
, evalEmulatorTrace
6567
, PrintEffect(..)
6668
, runEmulatorTraceEff
6769
, runEmulatorTraceIO
@@ -96,7 +98,7 @@ import Wallet.Emulator.MultiAgent (EmulatorEvent,
9698
EmulatorState (_chainState, _walletStates), MultiAgentControlEffect,
9799
MultiAgentEffect, _eteEmulatorTime, _eteEvent, schedulerEvent)
98100
import Wallet.Emulator.Stream (EmulatorConfig (_initialChainState, _params), EmulatorErr, foldEmulatorStreamM,
99-
initialChainState, initialDist, params, runTraceStream)
101+
initialChainState, initialDist, params, runTraceStream, runTraceStream')
100102
import Wallet.Emulator.Stream qualified
101103
import Wallet.Emulator.Wallet (Entity, balances)
102104
import Wallet.Emulator.Wallet qualified as Wallet
@@ -161,9 +163,9 @@ handleEmulatorTrace ::
161163
)
162164
=> Params
163165
-> EmulatorTrace a
164-
-> Eff (Reader ThreadId ': Yield (EmSystemCall effs EmulatorMessage) (Maybe EmulatorMessage) ': effs) ()
166+
-> Eff (Reader ThreadId ': Yield (EmSystemCall effs EmulatorMessage) (Maybe EmulatorMessage) ': effs) a
165167
handleEmulatorTrace Params{pNetworkId, pSlotConfig} action = do
166-
_ <- subsume @(Error EmulatorRuntimeError)
168+
result <- subsume @(Error EmulatorRuntimeError)
167169
. interpret (mapLog (UserThreadEvent . UserLog))
168170
. flip handleError (throwError . EmulatedWalletError)
169171
. reinterpret handleEmulatedWalletAPI
@@ -174,13 +176,21 @@ handleEmulatorTrace Params{pNetworkId, pSlotConfig} action = do
174176
. interpret (handleStartContract @_ @effs pNetworkId)
175177
$ raiseEnd action
176178
void $ exit @effs @EmulatorMessage
179+
pure result
177180

178181
-- | Run a 'Trace Emulator', streaming the log messages as they arrive
179182
runEmulatorStream :: forall effs a.
180183
EmulatorConfig
181184
-> EmulatorTrace a
182185
-> Stream (Of (LogMessage EmulatorEvent)) (Eff effs) (Maybe EmulatorErr, EmulatorState)
183-
runEmulatorStream conf = runTraceStream conf . interpretEmulatorTrace conf
186+
runEmulatorStream conf = runTraceStream conf . void . interpretEmulatorTrace conf
187+
188+
-- | Run a 'Trace Emulator', streaming the log messages as they arrive
189+
runEmulatorStream' :: forall effs a.
190+
EmulatorConfig
191+
-> EmulatorTrace a
192+
-> Stream (Of (LogMessage EmulatorEvent)) (Eff effs) (Either EmulatorErr a, EmulatorState)
193+
runEmulatorStream' conf = runTraceStream' conf . interpretEmulatorTrace conf
184194

185195
-- | Interpret a 'Trace Emulator' action in the multi agent and emulated
186196
-- blockchain effects.
@@ -194,12 +204,16 @@ interpretEmulatorTrace :: forall effs a.
194204
)
195205
=> EmulatorConfig
196206
-> EmulatorTrace a
197-
-> Eff effs ()
207+
-> Eff effs (Maybe a)
198208
interpretEmulatorTrace conf action =
199209
-- add a wait action to the beginning to ensure that the
200210
-- initial transaction gets validated before the wallets
201211
-- try to spend their funds
202-
let action' = Waiting.nextSlot >> action >> Waiting.nextSlot
212+
let action' = do
213+
void Waiting.nextSlot
214+
res <- action
215+
void Waiting.nextSlot
216+
pure res
203217
wallets = fromMaybe (Wallet.toMockWallet <$> CW.knownMockWallets) (preview (initialChainState . _Left . to Map.keys) conf)
204218
in
205219
evalState @EmulatorThreads mempty
@@ -236,11 +250,18 @@ defaultShowEvent = \case
236250
WalletEvent _ _ -> Nothing
237251
ev -> Just . renderString . layoutPretty defaultLayoutOptions . pretty $ ev
238252

253+
evalEmulatorTrace
254+
:: EmulatorConfig
255+
-> EmulatorTrace a
256+
-> Either EmulatorErr a
257+
evalEmulatorTrace cfg trace = case runEmulatorTrace' cfg trace of
258+
(_, r, _) -> r
259+
239260
-- | Run an emulator trace to completion, returning a tuple of the final state
240261
-- of the emulator, the events, and any error, if any.
241262
runEmulatorTrace
242263
:: EmulatorConfig
243-
-> EmulatorTrace ()
264+
-> EmulatorTrace a
244265
-> ([EmulatorEvent], Maybe EmulatorErr, EmulatorState)
245266
runEmulatorTrace cfg trace =
246267
(\(xs :> (y, z)) -> (xs, y, z))
@@ -249,6 +270,17 @@ runEmulatorTrace cfg trace =
249270
$ foldEmulatorStreamM (generalize list)
250271
$ runEmulatorStream cfg trace
251272

273+
runEmulatorTrace'
274+
:: EmulatorConfig
275+
-> EmulatorTrace a
276+
-> ([EmulatorEvent], Either EmulatorErr a, EmulatorState)
277+
runEmulatorTrace' cfg trace =
278+
(\(xs :> (y, z)) -> (xs, y, z))
279+
$ run
280+
$ runReader (initialDist cfg)
281+
$ foldEmulatorStreamM (generalize list)
282+
$ runEmulatorStream' cfg trace
283+
252284
-- | Run the emulator trace returning an effect that can be evaluated by
253285
-- interpreting the 'PrintEffect's.
254286
runEmulatorTraceEff :: forall effs. Member PrintEffect effs

plutus-contract/src/Plutus/Trace/Playground.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ interpretPlaygroundTrace :: forall w s e effs a.
151151
-> PlaygroundTrace a
152152
-> Eff effs ()
153153
interpretPlaygroundTrace conf contract wallets action =
154-
evalState @EmulatorThreads mempty
154+
void
155+
$ evalState @EmulatorThreads mempty
155156
$ evalState @(Map Wallet ContractInstanceId) Map.empty
156157
$ handleDeterministicIds
157158
$ interpret (mapLog (review schedulerEvent))

plutus-contract/src/Plutus/Trace/Scheduler.hs

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
{-# LANGUAGE TypeApplications #-}
1616
{-# LANGUAGE TypeFamilies #-}
1717
{-# LANGUAGE TypeOperators #-}
18+
{-# OPTIONS_GHC -Wno-orphans #-}
1819
{-
1920
2021
Implements a scheduler for cooperative multitasking. The scheduler supports
@@ -53,6 +54,7 @@ module Plutus.Trace.Scheduler(
5354
) where
5455

5556

57+
import Control.Applicative ((<|>))
5658
import Control.Lens hiding (Empty)
5759
import Control.Monad.Freer
5860
import Control.Monad.Freer.Coroutine
@@ -72,6 +74,8 @@ import Plutus.Trace.Tag (Tag)
7274
import Prettyprinter
7375
import Prettyprinter.Extras (PrettyShow (..), Tagged (..))
7476

77+
deriving instance Functor (Status effs a b)
78+
7579
{- Note [Thread Tag]
7680
7781
Within the scheduler, threads are identified by their 'ThreadId'. The thread
@@ -157,24 +161,27 @@ data WithPriority t
157161
, _thread :: t
158162
} deriving Functor
159163

160-
type SuspendedThread effs systemEvent = WithPriority (EmThread effs systemEvent)
164+
type SuspendedThread effs systemEvent a = WithPriority (EmThread effs systemEvent a)
161165

162166
type EmSystemCall effs systemEvent = WithPriority (SysCall effs systemEvent)
163167

164168
type AgentSystemCall systemEvent = WithPriority (MessageCall systemEvent)
165169

166170
-- | Thread that can be run by the scheduler
167-
data EmThread effs systemEvent =
171+
data EmThread effs systemEvent a =
168172
EmThread
169-
{ _continuation :: Maybe systemEvent -> Eff effs (Status effs (EmSystemCall effs systemEvent) (Maybe systemEvent) ()) -- ^ The continuation to be run when the thread is resumed.
173+
{ _continuation :: Maybe systemEvent -> Eff effs (Status effs (EmSystemCall effs systemEvent) (Maybe systemEvent) (Maybe a)) -- ^ The continuation to be run when the thread is resumed.
170174
, _threadId :: ThreadId -- ^ Thread ID
171175
, _tag :: Tag -- ^ Tag of the thread. See note [Thread Tag]
172176
}
173177

178+
mapEmThread :: (Maybe a -> Maybe b) -> EmThread effs systemEvent a -> EmThread effs systemEvent b
179+
mapEmThread f EmThread{_continuation, _threadId, _tag} = EmThread{ _threadId, _tag, _continuation = fmap (fmap (fmap f)) _continuation }
180+
174181
-- | The system calls we can make to the scheduler, affecting the the threads
175182
-- that are currently running.
176183
data ThreadCall effs systemEvent
177-
= Fork (ThreadId -> SuspendedThread effs systemEvent) -- ^ Start a new thread with a new thread ID.
184+
= Fork (ThreadId -> SuspendedThread effs systemEvent ()) -- ^ Start a new thread with a new thread ID.
178185
| Thaw ThreadId -- ^ Unfreeze a thread.
179186
| Exit -- ^ Terminate the scheduler.
180187

@@ -190,11 +197,11 @@ makePrisms ''MessageCall
190197
makePrisms ''ThreadCall
191198

192199
-- | Scheduler state
193-
data SchedulerState effs systemEvent
200+
data SchedulerState effs systemEvent a
194201
= SchedulerState
195-
{ _normalPrio :: Seq (EmThread effs systemEvent) -- ^ Threads running at normal priority
196-
, _sleeping :: Seq (EmThread effs systemEvent) -- ^ Sleeping threads (waiting for an external event)
197-
, _frozen :: Seq (EmThread effs systemEvent) -- ^ Frozen threads (will not be resumed until they are explicitly unfrozen)
202+
{ _normalPrio :: Seq (EmThread effs systemEvent a) -- ^ Threads running at normal priority
203+
, _sleeping :: Seq (EmThread effs systemEvent a) -- ^ Sleeping threads (waiting for an external event)
204+
, _frozen :: Seq (EmThread effs systemEvent a) -- ^ Frozen threads (will not be resumed until they are explicitly unfrozen)
198205
, _lastThreadId :: ThreadId -- ^ Last thread id assigned to a thread
199206
, _mailboxes :: HashMap ThreadId (Seq systemEvent) -- ^ The mailboxes of all active threads.
200207
, _activeThreads :: Map Tag (HashSet ThreadId) -- ^ Map of tags to thread IDs. See note [Thread Tag]
@@ -204,16 +211,16 @@ makeLenses ''SchedulerState
204211

205212
-- | Remove a thread from the set of active threads. Usually called when the
206213
-- thread is finished.
207-
removeActiveThread :: ThreadId -> SchedulerState effs systemEvent -> SchedulerState effs systemEvent
214+
removeActiveThread :: ThreadId -> SchedulerState effs systemEvent a -> SchedulerState effs systemEvent a
208215
removeActiveThread tid = over (activeThreads . mapped) (HashSet.delete tid)
209216

210217
-- | A suspended thread with a priority and the thread itself.
211-
suspendThread :: Priority -> EmThread effs systemEvent -> SuspendedThread effs systemEvent
218+
suspendThread :: Priority -> EmThread effs systemEvent a -> SuspendedThread effs systemEvent a
212219
suspendThread = WithPriority
213220

214221
-- | Make a thread with the given priority from an action. This is a
215222
-- convenience for defining 'SimulatorInterpreter' values.
216-
mkThread :: Tag -> Priority -> Eff (Reader ThreadId ': Yield (EmSystemCall effs systemEvent) (Maybe systemEvent) ': effs) () -> ThreadId -> SuspendedThread effs systemEvent
223+
mkThread :: Tag -> Priority -> Eff (Reader ThreadId ': Yield (EmSystemCall effs systemEvent) (Maybe systemEvent) ': effs) (Maybe a) -> ThreadId -> SuspendedThread effs systemEvent a
217224
mkThread tag prio action tid =
218225
let action' = runReader tid action
219226
in WithPriority
@@ -248,7 +255,7 @@ fork :: forall effs systemEvent effs2.
248255
-> Priority -- ^ Priority of the new thread.
249256
-> Eff (Reader ThreadId ': Yield (EmSystemCall effs systemEvent) (Maybe systemEvent) ': effs) ()
250257
-> Eff effs2 (Maybe systemEvent)
251-
fork tag prio action = mkSysCall prio (Right $ Fork $ mkThread tag prio action)
258+
fork tag prio action = mkSysCall prio (Right $ Fork $ mkThread tag prio $ Just <$> action)
252259

253260
-- | Suspend the current thread
254261
sleep :: forall effs systemEvent effs2.
@@ -271,59 +278,60 @@ initialThreadTag = "initial thread"
271278
-- effect using the scheduler, see note [Scheduler]. 'runThreads' only
272279
-- returns when all threads are finished.
273280
runThreads ::
274-
forall effs systemEvent.
281+
forall a effs systemEvent.
275282
( Eq systemEvent
276283
, Member (LogMsg SchedulerLog) effs
277284
)
278-
=> Eff (Reader ThreadId ': Yield (EmSystemCall effs systemEvent) (Maybe systemEvent) ': effs) ()
279-
-> Eff effs ()
285+
=> Eff (Reader ThreadId ': Yield (EmSystemCall effs systemEvent) (Maybe systemEvent) ': effs) a
286+
-> Eff effs (Maybe a)
280287
runThreads e = do
281288
k <- runC $ runReader initialThreadId e
282289
case k of
283-
Done () -> pure ()
290+
Done a -> pure $ Just a
284291
Continue _ k' ->
285-
let initialThread = EmThread{_continuation = k', _threadId = initialThreadId, _tag = initialThreadTag}
286-
in loop
292+
let initialThread = EmThread{_continuation = fmap (fmap Just) . k', _threadId = initialThreadId, _tag = initialThreadTag}
293+
in loop Nothing
287294
$ initialState
288295
& activeThreads . at initialThreadTag . non mempty %~ HashSet.insert initialThreadId
289296
& mailboxes . at initialThreadId ?~ Seq.empty
290297
& (fst . nextThreadId)
291298
& enqueue (suspendThread Normal initialThread)
292299

293300
-- | Run the threads that are scheduled in a 'SchedulerState' to completion.
294-
loop :: forall effs systemEvent.
301+
loop :: forall a effs systemEvent.
295302
( Eq systemEvent
296303
, Member (LogMsg SchedulerLog) effs
297304
)
298-
=> SchedulerState effs systemEvent
299-
-> Eff effs ()
300-
loop s = do
305+
=> Maybe a
306+
-> SchedulerState effs systemEvent a
307+
-> Eff effs (Maybe a)
308+
loop ma s = do
301309
case dequeue s of
302310
AThread EmThread{_continuation, _threadId, _tag} event schedulerState prio -> do
303311
let mkLog e = SchedulerLog{slEvent=e, slThread=_threadId, slPrio=prio, slTag = _tag}
304312
result <- _continuation event
305313
case result of
306-
Done () -> loop $ schedulerState & removeActiveThread _threadId
314+
Done ma' -> loop (ma <|> ma') $ schedulerState & removeActiveThread _threadId
307315
Continue WithPriority{_priority, _thread=sysCall} k -> do
308316
let thisThread = suspendThread _priority EmThread{_threadId=_threadId, _continuation=k, _tag = _tag}
309317
newState <- schedulerState & enqueue thisThread & handleSysCall sysCall
310318
case newState of
311-
Left r -> logDebug (mkLog $ Stopped r)
312-
Right newState' -> loop newState'
313-
_ -> pure ()
319+
Left r -> logDebug (mkLog $ Stopped r) >> pure ma
320+
Right newState' -> loop ma newState'
321+
_ -> pure ma
314322

315323
-- | Deal with a system call from a running thread.
316324
handleSysCall ::
317325
( Eq systemEvent
318326
, Member (LogMsg SchedulerLog) effs
319327
)
320328
=> SysCall effs systemEvent
321-
-> SchedulerState effs systemEvent
322-
-> Eff effs (Either StopReason (SchedulerState effs systemEvent))
329+
-> SchedulerState effs systemEvent a
330+
-> Eff effs (Either StopReason (SchedulerState effs systemEvent a))
323331
handleSysCall sysCall schedulerState = case sysCall of
324332
Right (Fork newThread) -> do
325333
let (schedulerState', tid) = nextThreadId schedulerState
326-
t = newThread tid
334+
t = mapEmThread (const Nothing) <$> newThread tid
327335
tag = _tag $ _thread t
328336
newState = enqueue t schedulerState'
329337
& activeThreads . at tag . non mempty %~ HashSet.insert tid
@@ -344,15 +352,15 @@ handleSysCall sysCall schedulerState = case sysCall of
344352

345353

346354
-- | Return a fresh thread ID and increment the counter
347-
nextThreadId :: SchedulerState effs systemEvent -> (SchedulerState effs systemEvent, ThreadId)
355+
nextThreadId :: SchedulerState effs systemEvent a -> (SchedulerState effs systemEvent a, ThreadId)
348356
nextThreadId s = (s & lastThreadId %~ ThreadId . succ . unThreadId, s ^. lastThreadId)
349357

350358
-- | State of the scheduler before any threads are run.
351-
initialState :: SchedulerState effs systemEvent
359+
initialState :: SchedulerState effs systemEvent a
352360
initialState = SchedulerState Seq.empty Seq.empty Seq.empty initialThreadId HashMap.empty Map.empty
353361

354362
-- | Add a suspended thread to the queue.
355-
enqueue :: SuspendedThread effs systemEvent -> SchedulerState effs systemEvent -> SchedulerState effs systemEvent
363+
enqueue :: SuspendedThread effs systemEvent a -> SchedulerState effs systemEvent a -> SchedulerState effs systemEvent a
356364
enqueue WithPriority {_priority, _thread} s =
357365
case _priority of
358366
Normal -> s & normalPrio %~ (|> _thread)
@@ -361,11 +369,11 @@ enqueue WithPriority {_priority, _thread} s =
361369

362370
-- | Result of calling 'dequeue'. Either a thread that is ready to receive a
363371
-- message, or no more threads.
364-
data SchedulerDQResult effs systemEvent
365-
= AThread (EmThread effs systemEvent) (Maybe systemEvent) (SchedulerState effs systemEvent) Priority
372+
data SchedulerDQResult effs systemEvent a
373+
= AThread (EmThread effs systemEvent a) (Maybe systemEvent) (SchedulerState effs systemEvent a) Priority
366374
| NoMoreThreads
367375

368-
dequeue :: SchedulerState effs systemEvent -> SchedulerDQResult effs systemEvent
376+
dequeue :: SchedulerState effs systemEvent a -> SchedulerDQResult effs systemEvent a
369377
dequeue s = case dequeueThread s of
370378
Nothing -> NoMoreThreads
371379
Just (s', thread, prio) -> case dequeueMessage s' (_threadId thread) of
@@ -374,7 +382,7 @@ dequeue s = case dequeueThread s of
374382

375383
-- | Find the next thread that is ready to be resumed.
376384
-- See note [Thread Priority]
377-
dequeueThread :: SchedulerState effs systemEvent -> Maybe (SchedulerState effs systemEvent, EmThread effs systemEvent, Priority)
385+
dequeueThread :: SchedulerState effs systemEvent a-> Maybe (SchedulerState effs systemEvent a, EmThread effs systemEvent a, Priority)
378386
dequeueThread s =
379387
case s ^. normalPrio of
380388
x :<| xs -> Just (s & normalPrio .~ xs, x, Normal)
@@ -383,7 +391,7 @@ dequeueThread s =
383391
Empty -> Nothing
384392

385393
-- | Get the first message for the thread.
386-
dequeueMessage :: SchedulerState effs systemEvent -> ThreadId -> Maybe (SchedulerState effs systemEvent, systemEvent)
394+
dequeueMessage :: SchedulerState effs systemEvent a -> ThreadId -> Maybe (SchedulerState effs systemEvent a, systemEvent)
387395
dequeueMessage s i = do
388396
mailbox <- s ^. mailboxes . at i
389397
(x, xs) <- case mailbox of { Empty -> Nothing; x :<| xs -> Just (x, xs) }

plutus-contract/src/Wallet/Emulator/Stream.hs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module Wallet.Emulator.Stream(
1919
, initialState
2020
, params
2121
, runTraceStream
22+
, runTraceStream'
2223
-- * Stream manipulation
2324
, takeUntilSlot
2425
, filterLogLevel
@@ -120,8 +121,26 @@ runTraceStream :: forall effs.
120121
, Error EmulatorRuntimeError
121122
] ()
122123
-> Stream (Of (LogMessage EmulatorEvent)) (Eff effs) (Maybe EmulatorErr, EmulatorState)
123-
runTraceStream conf@EmulatorConfig{_params} =
124-
fmap (first (either Just (const Nothing)))
124+
runTraceStream conf =
125+
fmap (first handleResult) . runTraceStream' conf . fmap Just
126+
where
127+
handleResult (Right _) = Nothing
128+
handleResult (Left InitialThreadFailedToReturn) = Nothing
129+
handleResult (Left err) = Just err
130+
131+
runTraceStream' :: forall effs a.
132+
EmulatorConfig
133+
-> Eff '[ State EmulatorState
134+
, LogMsg EmulatorEvent'
135+
, MultiAgentEffect
136+
, MultiAgentControlEffect
137+
, ChainEffect
138+
, ChainControlEffect
139+
, Error EmulatorRuntimeError
140+
] (Maybe a)
141+
-> Stream (Of (LogMessage EmulatorEvent)) (Eff effs) (Either EmulatorErr a, EmulatorState)
142+
runTraceStream' conf@EmulatorConfig{_params} =
143+
fmap (first $ either Left $ maybe (Left InitialThreadFailedToReturn) Right)
125144
. S.hoist (pure . run)
126145
. runStream @(LogMessage EmulatorEvent) @_ @'[]
127146
. runState (initialState conf)
@@ -183,6 +202,7 @@ data EmulatorErr =
183202
| ChainIndexErr ChainIndexError
184203
| AssertionErr EM.AssertionError
185204
| InstanceErr EmulatorRuntimeError
205+
| InitialThreadFailedToReturn
186206
deriving (Show)
187207

188208
handleLogCoroutine :: forall e effs.

0 commit comments

Comments
 (0)