Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
nhenin committed May 30, 2023
1 parent 0150b1e commit ef38fab
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 151 deletions.
1 change: 1 addition & 0 deletions marlowe-runtime-web/marlowe-runtime-web.cabal
Expand Up @@ -92,6 +92,7 @@ library server
Language.Marlowe.Runtime.Web.Server.REST
Language.Marlowe.Runtime.Web.Server.REST.ApiError
Language.Marlowe.Runtime.Web.Server.REST.Contracts
Language.Marlowe.Runtime.Web.Server.REST.Contracts.Possibilities
Language.Marlowe.Runtime.Web.Server.REST.Transactions
Language.Marlowe.Runtime.Web.Server.REST.Withdrawals
Language.Marlowe.Runtime.Web.Server.SyncClient
Expand Down
@@ -0,0 +1,50 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}

-- | This module defines a server for the /contracts REST API.

module Language.Marlowe.Runtime.Web.Server.REST.Contracts.Possibilities
( get
) where

import Cardano.Api (BabbageEra, TxBody, getTxBody, makeSignedTransaction)
import qualified Cardano.Api as Cardano
import Cardano.Ledger.Alonzo.TxWitness (TxWitness(TxWitness))
import Control.Monad (unless)
import Data.Aeson (Value(Null))
import Data.List.NonEmpty as NEL
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import qualified Data.Set as Set
import Data.Text (Text)
import Language.Marlowe (Environment)
import Language.Marlowe.Core.V1.Semantics.Possibilities (Possibilities, possibilities)
import Language.Marlowe.Protocol.Query.Types (ContractFilter(..), Page(..), SomeContractState(SomeContractState))
import Language.Marlowe.Runtime.ChainSync.Api (Lovelace(..))
import Language.Marlowe.Runtime.Core.Api
(ContractId, MarloweMetadataTag(..), MarloweTransactionMetadata(..), MarloweVersion(..), SomeMarloweVersion(..))
import Language.Marlowe.Runtime.Transaction.Api (ContractCreated(..), WalletAddresses(..))
import qualified Language.Marlowe.Runtime.Transaction.Api as Tx
import Language.Marlowe.Runtime.Web hiding (Unsigned)
import Language.Marlowe.Runtime.Web.Server.DTO
import Language.Marlowe.Runtime.Web.Server.Monad
(AppM, createContract, loadContract, loadContractHeaders, submitContract)
import Language.Marlowe.Runtime.Web.Server.REST.ApiError
(ApiError(ApiError), badRequest', notFound', rangeNotSatisfiable', throwDTOError)
import qualified Language.Marlowe.Runtime.Web.Server.REST.ApiError as ApiError
import qualified Language.Marlowe.Runtime.Web.Server.REST.Transactions as Transactions
import Language.Marlowe.Runtime.Web.Server.TxClient (TempTx(TempTx), TempTxStatus(Unsigned))
import Language.Marlowe.Runtime.Web.Server.Util (makeSignedTxWithWitnessKeys)
import Servant
import Servant.Pagination

get :: Environment -> TxOutRef -> AppM [Possibilities]
get environment contractId = do
contractId' <- fromDTOThrow (badRequest' "Invalid contract id value") contractId
loadContract contractId' >>= \case
Nothing -> throwError $ notFound' "Contract not found"
Just eitherTmpContractStateorContractState -> do
let ContractState {state,initialContract,currentContract} = either toDTO toDTO eitherTmpContractStateorContractState
pure $ NEL.toList $ fromMaybe [] $ possibilities environment state (fromMaybe initialContract currentContract)


7 changes: 6 additions & 1 deletion marlowe-runtime-web/src/Language/Marlowe/Runtime/Web/API.hs
Expand Up @@ -51,6 +51,7 @@ import GHC.Exts (IsList(..))
import GHC.Generics (Generic)
import GHC.Show (showSpace)
import GHC.TypeLits (KnownSymbol, symbolVal)
import Language.Marlowe.Core.V1.Semantics.Possibilities (Possibilities)
import Language.Marlowe.Runtime.Web.Types
import Network.HTTP.Media ((//))
import Servant
Expand Down Expand Up @@ -122,7 +123,8 @@ type PostContractsAPI
-- | /contracts/:contractId sup-API
type ContractAPI = GetContractAPI
:<|> PutSignedTxAPI
:<|> "transactions" :> TransactionsAPI
:<|> "possibilities" :> GETPossibilitiesAPI
:<|> "transactions" :> TransactionsAPI

-- | GET /contracts/:contractId sub-API
type GetContractAPI = Get '[JSON] GetContractResponse
Expand All @@ -134,6 +136,9 @@ instance HasNamedLink ContractState API "transactions" where
"contracts" :> Capture "contractId" TxOutRef :> "transactions" :> GetTransactionsAPI
namedLink _ _ mkLink ContractState{..} = guard (status == Confirmed) $> mkLink contractId

-- | GET /contracts/:contractId/possibilities sub-API
type GETPossibilitiesAPI = Get '[JSON] [Possibilities]

-- | /contracts/:contractId/transactions sup-API
type TransactionsAPI = GetTransactionsAPI
:<|> PostTransactionsAPI
Expand Down
10 changes: 5 additions & 5 deletions marlowe-runtime-web/src/Language/Marlowe/Runtime/Web/Client.hs
Expand Up @@ -187,7 +187,7 @@ getTransactions
getTransactions contractId range = do
let contractsClient :<|> _ = client
let _ :<|> _ :<|> contractApi = contractsClient
let _ :<|> _ :<|> getTransactions' :<|> _ = contractApi contractId
let _ :<|> _ :<|> _ :<|> getTransactions' :<|> _ = contractApi contractId
response <- getTransactions' $ putRange <$> range
totalCount <- reqHeaderValue $ lookupResponseHeader @"Total-Count" response
nextRanges <- headerValue $ lookupResponseHeader @"Next-Range" response
Expand All @@ -208,7 +208,7 @@ postTransaction
postTransaction changeAddress otherAddresses collateralUtxos contractId request = do
let contractsClient :<|> _ = client
let _ :<|> _ :<|> contractApi = contractsClient
let _ :<|> _ :<|> _ :<|> (postTransaction' :<|> _) :<|> _ = contractApi contractId
let _ :<|> _ :<|> _ :<|> _ :<|> (postTransaction' :<|> _) :<|> _ = contractApi contractId
response <- postTransaction'
request
changeAddress
Expand All @@ -225,7 +225,7 @@ postTransactionCreateTx
-> ClientM (ApplyInputsTxEnvelope CardanoTx)
postTransactionCreateTx changeAddress otherAddresses collateralUtxos contractId request = do
let (_ :<|> _ :<|> contractApi) :<|> _ = client
let _ :<|> _ :<|> _ :<|> (_ :<|> postTransactionCreateTx') :<|> _ = contractApi contractId
let _ :<|> _ :<|> _ :<|> _ :<|> (_ :<|> postTransactionCreateTx') :<|> _ = contractApi contractId
response <- postTransactionCreateTx'
request
changeAddress
Expand All @@ -238,15 +238,15 @@ getTransaction :: TxOutRef -> TxId -> ClientM Tx
getTransaction contractId transactionId = do
let contractsClient :<|> _ = client
let _ :<|> _ :<|> contractApi = contractsClient
let _ :<|> _ :<|> _ :<|> _ :<|> transactionApi = contractApi contractId
let _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> transactionApi = contractApi contractId
let getTransaction' :<|> _ = transactionApi transactionId
retractLink . retractLink <$> getTransaction'

putTransaction :: TxOutRef -> TxId -> TextEnvelope -> ClientM ()
putTransaction contractId transactionId tx = do
let contractsClient :<|> _ = client
let _ :<|> _ :<|> contractApi = contractsClient
let _ :<|> _ :<|> _ :<|> _ :<|> transactionApi = contractApi contractId
let _ :<|> _ :<|> _ :<|> _ :<|> _ :<|> transactionApi = contractApi contractId
let _ :<|> putTransaction' = transactionApi transactionId
void $ putTransaction' tx

Expand Down
2 changes: 1 addition & 1 deletion marlowe/marlowe-cardano.cabal
Expand Up @@ -96,7 +96,7 @@ library
Language.Marlowe.Core.V1.Plate
Language.Marlowe.Core.V1.Semantics
Language.Marlowe.Core.V1.Semantics.Types
Language.Marlowe.Core.V1.Semantics.Possibilities
Language.Marlowe.Core.V1.Semantics.Next
Language.Marlowe.Core.V1.Semantics.Types.Address
Language.Marlowe.Extended.V1
Language.Marlowe.Extended.V1.Metadata.Types
Expand Down
150 changes: 150 additions & 0 deletions marlowe/src/Language/Marlowe/Core/V1/Semantics/Next.hs
@@ -0,0 +1,150 @@
-----------------------------------------------------------------------------
--
-- Module : $Headers
-- License : Apache 2.0
--
-- Stability : Experimental
-- Portability : Portable
--
-- | Types for Marlowe semantics
--
-----------------------------------------------------------------------------


{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}


module Language.Marlowe.Core.V1.Semantics.Next
( Next(..)
, NextActionPossibility(..)
, next
) where

import Control.Applicative ((<*>), (<|>))
import qualified Data.Aeson as JSON
import Data.Aeson.Types hiding (Error, Value)
import Data.ByteString.Base16.Aeson (EncodeBase16(..))
import Data.List.NonEmpty as NEL
import Data.Text (pack)
import Deriving.Aeson
import Language.Marlowe.Core.V1.Semantics
import Language.Marlowe.Core.V1.Semantics.Types
import Language.Marlowe.Pretty (Pretty(..))
import PlutusTx.Prelude hiding (encodeUtf8, mapM, (<$>), (<*>), (<>))
import Prelude ((<$>))
import qualified Prelude as Haskell

-- | Description on What can be done next
data Next
= WaitingForInput (NonEmpty NextAction)
| ContractClosed
| SuspendedOnATimeOut
deriving stock (Haskell.Show,Generic,Haskell.Eq,Haskell.Ord)
deriving anyclass (Pretty)

data NextContinuation
= NextContract Contract
| NextMerkleizedContract BuiltinByteString
deriving stock (Haskell.Show,Generic,Haskell.Eq,Haskell.Ord)
deriving anyclass (Pretty)

instance FromJSON NextContinuation where
parseJSON (Object v)
= NextContract <$> v .: "contract"
<|> NextMerkleizedContract <$> do
mt <- v .: "merkleized_contract_hash"
EncodeBase16 bs <- parseJSON mt
return $ toBuiltin bs
parseJSON _ = Haskell.fail "NextAction must be an object with 2 fields \"action_possibility\" and \"continuation\""

data NextAction = NextAction NextActionPossibility NextContinuation
deriving stock (Haskell.Show,Generic,Haskell.Eq,Haskell.Ord)
deriving anyclass (Pretty)

instance FromJSON NextAction where
parseJSON (Object v) = NextAction <$> v .: "action_possibility" <*> v .: "continuation"
parseJSON _ = Haskell.fail "NextAction must be an object with 2 fields \"action_possibility\" and \"continuation\""

-- | an Action that can be done out of several possible alternatives.
data NextActionPossibility
= CanDeposit Party AccountId Token Integer
| CanChoose ChoiceId [Bound]
| CanNotify
| CanAdvance
deriving stock (Haskell.Show,Haskell.Eq,Haskell.Ord,Generic)
deriving anyclass (Pretty)


instance FromJSON NextActionPossibility where
parseJSON (String "can_notify") = return CanNotify
parseJSON (String "can_advance") = return CanAdvance
parseJSON (Object v) =
CanChoose
<$> v .: "can_choose_id"
<*> v .: "within_bounds"
<|> CanDeposit
<$> v .: "party"
<*> v .: "into_account"
<*> v .: "of_token"
<*> v .: "can_deposit"
parseJSON _ = Haskell.fail "Action Possibility must be either an object or the string \"can_notify\" or \"can_advance\""

instance ToJSON NextActionPossibility where
toJSON CanNotify = JSON.String $ pack "can_notify"
toJSON CanAdvance = JSON.String $ pack "can_advance"
toJSON (CanDeposit party accountId token quantity) = object
[ "party" .= party
, "can_deposit" .= quantity
, "of_token" .= token
, "into_account" .= accountId
]
toJSON (CanChoose choiceId bounds) = object
[ "for_choice" .= choiceId
, "can_choose_between" .= bounds
]


next :: Environment -> State -> Contract -> Next
next _ _ Close = ContractClosed
next environment _ (When _ timeout continuation) | isTimedOut timeout environment = WaitingForInput $ (NextAction CanAdvance $ NextContract continuation):| []
next _ _ (When [] _ _) = SuspendedOnATimeOut
next environment state (When (x:xs) _ _) = WaitingForInput . caseContractsToNextAction environment state $ x :| xs
next _ _ (Pay _ _ _ _ continuation) = WaitingForInput $ (NextAction CanAdvance $ NextContract continuation):| []
next _ _ (If _ _ continuation) = WaitingForInput $ (NextAction CanAdvance $ NextContract continuation):| []
next _ _ (Let _ _ continuation) = WaitingForInput $ (NextAction CanAdvance $ NextContract continuation):| []
next _ _ (Assert _ continuation) = WaitingForInput $ (NextAction CanAdvance $ NextContract continuation):| []

caseContractsToNextAction :: Environment -> State -> NonEmpty (Case Contract) -> NonEmpty NextAction
caseContractsToNextAction environment state
= (caseContractToNextAction environment state <$>)

caseContractToNextAction :: Environment -> State -> Case Contract -> NextAction
caseContractToNextAction environment state
= \case
(Case action contract) -> NextAction (actionToNextPossibilities environment state action)(NextContract contract)
(MerkleizedCase action hash) -> NextAction (actionToNextPossibilities environment state action)(NextMerkleizedContract hash)

actionToNextPossibilities :: Environment -> State -> Action -> NextActionPossibility
actionToNextPossibilities environment state = \case
(Deposit accountId party token value)
-> CanDeposit
accountId
party
token
(evalValue environment state value)
(Choice choiceId bounds) -> CanChoose choiceId bounds
(Notify _) -> CanNotify

0 comments on commit ef38fab

Please sign in to comment.