@@ -6,10 +6,11 @@ module BotPlutusInterface.Server (
66 WebSocketEndpoint ,
77 ActivateContractEndpoint ,
88 RawTxEndpoint ,
9+ TxIdCapture (TxIdCapture ),
910) where
1011
1112import BotPlutusInterface.Contract (runContract )
12- import BotPlutusInterface.Files (txFileName )
13+ import BotPlutusInterface.Files (txFileName , txIdToText )
1314import BotPlutusInterface.Types (
1415 AppState (AppState ),
1516 ContractEnvironment (.. ),
@@ -25,15 +26,18 @@ import Control.Monad.Error.Class (throwError)
2526import Control.Monad.IO.Class (liftIO )
2627import Data.Aeson (FromJSON , ToJSON (toJSON ))
2728import Data.Aeson qualified as JSON
29+ import Data.Bifunctor (bimap )
2830import Data.ByteString.Lazy qualified as LBS
2931import Data.Either.Combinators (leftToMaybe )
3032import Data.Kind (Type )
3133import Data.Map qualified as Map
3234import Data.Maybe (catMaybes )
3335import Data.Proxy (Proxy (Proxy ))
3436import Data.Row (Row )
35- import Data.Text (Text , unpack )
37+ import Data.Text (Text , pack , unpack )
38+ import Data.Text.Encoding (encodeUtf8 )
3639import Data.UUID.V4 qualified as UUID
40+ import Ledger.TxId (TxId (TxId ))
3741import Network.WebSockets (
3842 Connection ,
3943 PendingConnection ,
@@ -56,7 +60,19 @@ import Plutus.PAB.Webserver.Types (
5660 ContractActivationArgs (.. ),
5761 InstanceStatusToClient (ContractFinished , NewObservableState ),
5862 )
59- import Servant.API (Capture , Get , JSON , Post , ReqBody , (:<|>) (.. ), (:>) )
63+ import Plutus.V1.Ledger.Bytes (LedgerBytes (LedgerBytes ), fromHex )
64+ import PlutusTx.Prelude (lengthOfByteString )
65+ import Servant.API (
66+ Capture ,
67+ FromHttpApiData (parseUrlPiece ),
68+ Get ,
69+ JSON ,
70+ Post ,
71+ ReqBody ,
72+ ToHttpApiData (toUrlPiece ),
73+ (:<|>) ((:<|>) ),
74+ (:>) ,
75+ )
6076import Servant.API.WebSocket (WebSocketPending )
6177import Servant.Server (Application , Handler , Server , err404 , serve )
6278import System.Directory (canonicalizePath , doesFileExist , makeAbsolute )
@@ -85,9 +101,26 @@ type ActivateContractEndpoint a =
85101
86102type RawTxEndpoint =
87103 " raw-tx"
88- :> Capture " txId " Text
104+ :> Capture " tx-id " TxIdCapture
89105 :> Get '[JSON ] RawTx
90106
107+ newtype TxIdCapture = TxIdCapture TxId
108+
109+ instance FromHttpApiData TxIdCapture where
110+ parseUrlPiece :: Text -> Either Text TxIdCapture
111+ parseUrlPiece t = bimap pack bytesToTxIdCapture $ checkLength =<< fromHex (encodeUtf8 t)
112+ where
113+ checkLength :: LedgerBytes -> Either String LedgerBytes
114+ checkLength b@ (LedgerBytes bs) =
115+ if lengthOfByteString bs == 32
116+ then Right b
117+ else Left " Invalid length"
118+ bytesToTxIdCapture :: LedgerBytes -> TxIdCapture
119+ bytesToTxIdCapture (LedgerBytes b) = TxIdCapture $ TxId b
120+
121+ instance ToHttpApiData TxIdCapture where
122+ toUrlPiece (TxIdCapture txId) = txIdToText txId
123+
91124server :: HasDefinitions t => PABConfig -> AppState -> Server (API t )
92125server pabConfig state =
93126 websocketHandler state
@@ -234,16 +267,16 @@ handleContract pabConf state@(AppState st) contract = liftIO $ do
234267 pure contractInstanceID
235268
236269-- | This handler will allow to retrieve raw transactions from the pcTxFileDir if pcEnableTxEndpoint is True
237- rawTxHandler :: PABConfig -> Text -> Handler RawTx
238- rawTxHandler config txId = do
270+ rawTxHandler :: PABConfig -> TxIdCapture -> Handler RawTx
271+ rawTxHandler config ( TxIdCapture txId) = do
239272 -- Check that endpoint is enabled
240273 assert config. pcEnableTxEndpoint
241274 -- Absolute path to pcTxFileDir that is specified in the config
242275 txFolderPath <- liftIO $ makeAbsolute (unpack config. pcTxFileDir)
243276
244277 -- Add/Set .raw extension on path
245278 let suppliedPath :: FilePath
246- suppliedPath = txFolderPath </> unpack (txFileName txId " . raw" )
279+ suppliedPath = txFolderPath </> unpack (txFileName txId " raw" )
247280 -- Resolve path indirections
248281 path <- liftIO $ canonicalizePath suppliedPath
249282 -- ensure it does not try to escape txFolderPath
0 commit comments