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

Commit b9f50e5

Browse files
Waiting for endpoint result in plutus-pab when querying contract instance status (#784)
Co-authored-by: etiennejf <jean-frederic.etienne@iohk.io>
1 parent 955249f commit b9f50e5

File tree

3 files changed

+58
-13
lines changed

3 files changed

+58
-13
lines changed

plutus-pab/src/Plutus/PAB/Core.hs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ module Plutus.PAB.Core
5656
, instanceState
5757
, observableState
5858
, waitForState
59+
, waitForInstanceState
60+
, waitForInstanceStateWithResult
5961
, waitForTxStatusChange
6062
, waitForTxOutStatusChange
6163
, activeEndpoints
@@ -83,7 +85,7 @@ module Plutus.PAB.Core
8385
, timed
8486
) where
8587

86-
import Control.Applicative (Alternative ((<|>)))
88+
import Control.Applicative (Alternative ((<|>)), empty)
8789
import Control.Concurrent.STM (STM)
8890
import Control.Concurrent.STM qualified as STM
8991
import Control.Lens (view)
@@ -112,7 +114,8 @@ import Plutus.Contract.Effects (ActiveEndpoint (ActiveEndpoint, aeDescription),
112114
import Plutus.Contract.Wallet (ExportTx)
113115
import Plutus.PAB.Core.ContractInstance (ContractInstanceMsg, ContractInstanceState)
114116
import Plutus.PAB.Core.ContractInstance qualified as ContractInstance
115-
import Plutus.PAB.Core.ContractInstance.STM (Activity (Active), BlockchainEnv, InstancesState, OpenEndpoint)
117+
import Plutus.PAB.Core.ContractInstance.STM (Activity (Active, Done, Stopped), BlockchainEnv, InstancesState,
118+
OpenEndpoint)
116119
import Plutus.PAB.Core.ContractInstance.STM qualified as Instances
117120
import Plutus.PAB.Effects.Contract (ContractDefinition, ContractEffect, ContractStore, PABContract (ContractDef),
118121
getState)
@@ -133,6 +136,7 @@ import Wallet.Emulator.MultiAgent (EmulatorEvent' (WalletEvent), EmulatorTimeEve
133136
import Wallet.Emulator.Wallet (Wallet, WalletEvent (GenericLog, RequestHandlerLog, TxBalanceLog), mockWalletAddress)
134137
import Wallet.Types (ContractActivityStatus, ContractInstanceId, EndpointDescription (EndpointDescription),
135138
NotificationError)
139+
import Wallet.Types qualified as Wallet
136140

137141
-- | Effects that are available in 'PABAction's.
138142
type PABEffects t env =
@@ -506,6 +510,42 @@ waitForState extract instanceId = do
506510
state <- stm
507511
maybe STM.retry pure (extract state)
508512

513+
514+
-- | Wait until the instance state of the instance satisfies a predicate and returns the activity status
515+
waitForInstanceState ::
516+
forall t env.
517+
(Instances.InstanceState -> STM (Maybe ContractActivityStatus)) ->
518+
ContractInstanceId ->
519+
PABAction t env ContractActivityStatus
520+
waitForInstanceState extract instanceId = do
521+
is <- instanceStateInternal instanceId
522+
liftIO $ STM.atomically $ do
523+
ms <- extract is
524+
maybe empty pure ms
525+
526+
-- | Wait until the instance state is updated with a response form an invoked endpoint.
527+
-- Note that the waiting is performed only when a contract is expected to end with a Done status, i.e.,
528+
-- no open endpoints available after invocation.
529+
waitForInstanceStateWithResult :: forall t env. ContractInstanceId -> PABAction t env ContractActivityStatus
530+
waitForInstanceStateWithResult instanceId = do
531+
-- retry query when waiting for a response from an invoked endpoint s.t.
532+
-- contract is supposed to end with status Done
533+
let parseStatus :: Activity -> ContractActivityStatus
534+
parseStatus = \case
535+
Active -> Wallet.Active
536+
Stopped -> Wallet.Stopped
537+
Done _ -> Wallet.Done
538+
let r_check :: Instances.InstanceState -> STM (Maybe ContractActivityStatus)
539+
r_check Instances.InstanceState{Instances.issEndpoints, Instances.issStatus, Instances.issObservableState} = do
540+
status <- STM.readTVar issStatus
541+
ostate <- STM.readTVar issObservableState
542+
currentEndpoints <- STM.readTVar issEndpoints
543+
case (status, ostate) of
544+
(Active, Just JSON.Null) | Map.null currentEndpoints ->
545+
pure Nothing
546+
_ -> pure $ Just $ parseStatus status
547+
waitForInstanceState r_check instanceId
548+
509549
-- | Wait for the transaction to be confirmed on the blockchain.
510550
waitForTxStatusChange :: forall t env. TxId -> PABAction t env TxStatus
511551
waitForTxStatusChange t = do

plutus-pab/src/Plutus/PAB/Effects/Contract.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ getState i =
170170
in send command
171171

172172
-- | All active contracts with their definitions
173+
-- WARNING : definition is misleading as this function is retrieving all instances (i.e., not only active ones),
174+
-- especially when the in memory database setting is used
175+
-- Indeed, handler defined in ContractStore ignores the status parameter given in GetContracts.
176+
-- Note also that a contract instance added in the db has active status set to True.
177+
-- This status is set to False only when the contract is explicitly stopped.
173178
getActiveContracts ::
174179
forall t effs.
175180
( Member (ContractStore t) effs

plutus-pab/src/Plutus/PAB/Webserver/Handler.hs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,17 @@ contractInstanceState
137137
=> ContractInstanceId
138138
-> PABAction t env (ContractInstanceClientState (Contract.ContractDef t))
139139
contractInstanceState i = do
140-
definition <- Contract.getDefinition @t i
141-
instWithStatuses <- Core.instancesWithStatuses
142-
case (definition, Map.lookup i instWithStatuses) of
143-
(Just ContractActivationArgs{caWallet, caID}, Just s) -> do
144-
let wallet = fromMaybe (knownWallet 1) caWallet
145-
yieldedExportedTxs <- Core.yieldedExportTxs i
146-
fmap ( fromInternalState caID i s wallet yieldedExportedTxs
147-
. fromResp
148-
. Contract.serialisableState (Proxy @t)
149-
) $ Contract.getState @t i
150-
_ -> throwError @PABError (ContractInstanceNotFound i)
140+
definition <- Contract.getDefinition @t i
141+
s <- Core.waitForInstanceStateWithResult i
142+
case definition of
143+
Just ContractActivationArgs{caWallet, caID} -> do
144+
let wallet = fromMaybe (knownWallet 1) caWallet
145+
yieldedExportedTxs <- Core.yieldedExportTxs i
146+
fmap ( fromInternalState caID i s wallet yieldedExportedTxs
147+
. fromResp
148+
. Contract.serialisableState (Proxy @t)
149+
) $ Contract.getState @t i
150+
_ -> throwError @PABError (ContractInstanceNotFound i)
151151

152152
callEndpoint :: forall t env. ContractInstanceId -> String -> JSON.Value -> PABAction t env ()
153153
callEndpoint a b v = Core.callEndpointOnInstance a b v >>= traverse_ (throwError @PABError . EndpointCallError)

0 commit comments

Comments
 (0)