Skip to content

Commit

Permalink
Start using Pact 5 CommandResult for /listen and /poll
Browse files Browse the repository at this point in the history
  • Loading branch information
edmundnoble committed Sep 18, 2024
1 parent fdf5caf commit 3df22fd
Show file tree
Hide file tree
Showing 14 changed files with 166 additions and 156 deletions.
2 changes: 1 addition & 1 deletion cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ source-repository-package
source-repository-package
type: git
location: https://github.com/kadena-io/pact-5.git
tag: bc63c5f1ddfdc3f82529a39448de8632ed4b17cb
tag: fb544f8c553078f071e3bf2225a94f7dbd28e6dc
--sha256: 16k2j2nlc8crwqr5018h8f0mavp99gprc2nq74c4bxq373c59dsa

source-repository-package
Expand Down
1 change: 1 addition & 0 deletions chainweb.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,7 @@ executable cwtool
, optparse-applicative >= 0.14
, pact
, pact-json
, pact-tng
, pact-tng:pact-request-api
, patience >= 0.3
, process >= 1.5
Expand Down
24 changes: 9 additions & 15 deletions src/Chainweb/Pact/RestAPI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ module Chainweb.Pact.RestAPI
, pactListenApi
, PactSendApi
, pactSendApi
, PactPollApi
, pactPollApi
, PactLocalWithQueryApi
, pactLocalWithQueryApi
, PactPollWithQueryApi
Expand Down Expand Up @@ -59,8 +57,7 @@ module Chainweb.Pact.RestAPI
import Data.Text (Text)

import qualified Pact.Types.Command as Pact
import Pact.Server.API as API
import Pact.Types.API (Poll, PollResponses)
import qualified Pact.Server.API as Pact4
import Pact.Utils.Servant

import Servant
Expand All @@ -74,6 +71,7 @@ import Chainweb.Pact.Types
import Chainweb.RestAPI.Utils
import Chainweb.SPV.PayloadProof
import Chainweb.Version
import qualified Pact.Core.Command.Server as Pact5

-- -------------------------------------------------------------------------- --
-- @POST /chainweb/<ApiVersion>/<ChainwebVersion>/chain/<ChainId>/pact/@
Expand All @@ -83,7 +81,7 @@ type PactApi_
= "pact"
:> "api"
:> "v1"
:> ( ApiSend
:> ( Pact4.ApiSend
:<|> PactPollWithQueryApi_
:<|> ApiListen
:<|> PactLocalWithQueryApi_
Expand All @@ -108,10 +106,11 @@ type PactV1ApiEndpoint (v :: ChainwebVersionT) (c :: ChainIdT) api
:> "v1"
:> api

type PactLocalApi v c = PactV1ApiEndpoint v c ApiLocal
type PactSendApi v c = PactV1ApiEndpoint v c ApiSend
type PactLocalApi v c = PactV1ApiEndpoint v c Pact4.ApiLocal
type PactSendApi v c = PactV1ApiEndpoint v c Pact4.ApiSend
type PactListenApi v c = PactV1ApiEndpoint v c ApiListen
type PactPollApi v c = PactV1ApiEndpoint v c ApiPoll

type ApiListen = ("listen" :> ReqBody '[PactJson] Pact5.ListenRequest :> Post '[PactJson] Pact5.ListenResponse)

pactLocalApi
:: forall (v :: ChainwebVersionT) (c :: ChainIdT)
Expand All @@ -128,11 +127,6 @@ pactListenApi
. Proxy (PactListenApi v c)
pactListenApi = Proxy

pactPollApi
:: forall (v :: ChainwebVersionT) (c :: ChainIdT)
. Proxy (PactPollApi v c)
pactPollApi = Proxy

-- -------------------------------------------------------------------------- --
-- POST Queries for Pact Local Pre-flight

Expand All @@ -157,8 +151,8 @@ pactLocalWithQueryApi = Proxy
type PactPollWithQueryApi_
= "poll"
:> QueryParam "confirmationDepth" ConfirmationDepth
:> ReqBody '[PactJson] Poll
:> Post '[PactJson] PollResponses
:> ReqBody '[PactJson] Pact5.PollRequest
:> Post '[PactJson] Pact5.PollResponse

type PactPollWithQueryApi v c = PactV1ApiEndpoint v c PactPollWithQueryApi_

Expand Down
37 changes: 9 additions & 28 deletions src/Chainweb/Pact/RestAPI/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ module Chainweb.Pact.RestAPI.Client
, pactSpv2ApiClient
, ethSpvApiClient_
, ethSpvApiClient
, pactPollApiClient_
, pactPollApiClient
, pactListenApiClient_
, pactListenApiClient
, pactSendApiClient_
Expand Down Expand Up @@ -51,6 +49,7 @@ import Chainweb.Pact.RestAPI.SPV
import Chainweb.Pact.Types
import Chainweb.SPV.PayloadProof
import Chainweb.Version
import qualified Pact.Core.Command.Server as Pact5

-- -------------------------------------------------------------------------- --
-- Pact Spv Transaction Output Proof Client
Expand Down Expand Up @@ -184,15 +183,15 @@ pactListenApiClient_
:: forall (v :: ChainwebVersionT) (c :: ChainIdT)
. KnownChainwebVersionSymbol v
=> KnownChainIdSymbol c
=> ListenerRequest
-> ClientM ListenResponse
=> Pact5.ListenRequest
-> ClientM Pact5.ListenResponse
pactListenApiClient_ = client (pactListenApi @v @c)

pactListenApiClient
:: ChainwebVersion
-> ChainId
-> ListenerRequest
-> ClientM ListenResponse
-> Pact5.ListenRequest
-> ClientM Pact5.ListenResponse
pactListenApiClient
(FromSingChainwebVersion (SChainwebVersion :: Sing v))
(FromSingChainId (SChainId :: Sing c))
Expand Down Expand Up @@ -222,38 +221,20 @@ pactSendApiClient
-- -------------------------------------------------------------------------- --
-- Pact Poll

pactPollApiClient_
:: forall (v :: ChainwebVersionT) (c :: ChainIdT)
. KnownChainwebVersionSymbol v
=> KnownChainIdSymbol c
=> Poll
-> ClientM PollResponses
pactPollApiClient_ = client (pactPollApi @v @c)

pactPollApiClient
:: ChainwebVersion
-> ChainId
-> Poll
-> ClientM PollResponses
pactPollApiClient
(FromSingChainwebVersion (SChainwebVersion :: Sing v))
(FromSingChainId (SChainId :: Sing c))
= pactPollApiClient_ @v @c

pactPollWithQueryApiClient_
:: forall (v :: ChainwebVersionT) (c :: ChainIdT)
. KnownChainwebVersionSymbol v
=> KnownChainIdSymbol c
=> Maybe ConfirmationDepth
-> Poll
-> ClientM PollResponses
-> Pact5.PollRequest
-> ClientM Pact5.PollResponse
pactPollWithQueryApiClient_ = client (pactPollWithQueryApi @v @c)

pactPollWithQueryApiClient
:: ChainwebVersion
-> ChainId
-> Maybe ConfirmationDepth
-> Poll
-> ClientM PollResponses
-> Pact5.PollRequest
-> ClientM Pact5.PollResponse
pactPollWithQueryApiClient (FromSingChainwebVersion (SChainwebVersion :: Sing v)) (FromSingChainId (SChainId :: Sing c)) confirmationDepth poll = do
pactPollWithQueryApiClient_ @v @c confirmationDepth poll
102 changes: 52 additions & 50 deletions src/Chainweb/Pact/RestAPI/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ import qualified Chainweb.CutDB as CutDB
import Chainweb.Graph
import Chainweb.Logger
import Chainweb.Mempool.Mempool
(InsertError(..), InsertType(..), MempoolBackend(..), TransactionHash(..), pact4RequestKeyToTransactionHash)
(InsertError(..), InsertType(..), MempoolBackend(..), TransactionHash(..), pact5RequestKeyToTransactionHash)
import Chainweb.Pact.RestAPI
import Chainweb.Pact.RestAPI.EthSpv
import Chainweb.Pact.RestAPI.SPV
Expand Down Expand Up @@ -128,13 +128,17 @@ import qualified Pact.Types.API as Pact4
import qualified Pact.Types.ChainId as Pact4
import qualified Pact.Types.Command as Pact4
import qualified Pact.Types.Hash as Pact4
import qualified Pact.Types.PactError as Pact4
import qualified Pact.Types.Pretty as Pact4

import qualified Pact.Core.Command.Types as Pact5
import qualified Pact.Core.StableEncoding as Pact5
import qualified Chainweb.Pact5.Transaction as Pact5
import qualified Chainweb.Pact5.Validations as Pact5
import Data.Coerce
import qualified Pact.Core.Command.Server as Pact5
import qualified Pact.Core.Evaluate as Pact5
import qualified Pact.Core.Errors as Pact5
import qualified Pact.Core.Hash as Pact5
import qualified Pact.Core.Gas as Pact5

-- -------------------------------------------------------------------------- --

Expand Down Expand Up @@ -297,13 +301,11 @@ pollHandler
-> PactExecutionService
-> MempoolBackend Pact4.UnparsedTransaction
-> Maybe ConfirmationDepth
-> Pact4.Poll
-> Handler Pact4.PollResponses
pollHandler logger cdb cid pact mem confDepth (Pact4.Poll request) = do
traverse_ validateRequestKey request

liftIO $! logg Info $ PactCmdLogPoll $ fmap Pact4.requestKeyToB16Text request
Pact4.PollResponses <$!> liftIO (internalPoll logger pdb bdb mem pact confDepth request)
-> Pact5.PollRequest
-> Handler Pact5.PollResponse
pollHandler logger cdb cid pact mem confDepth (Pact5.PollRequest request) = do
liftIO $! logg Info $ PactCmdLogPoll $ fmap Pact5.requestKeyToB64Text request
Pact5.PollResponse <$!> liftIO (internalPoll logger pdb bdb mem pact confDepth request)
where
pdb = view CutDB.cutDbPayloadDb cdb
bdb = fromJuste $ preview (CutDB.cutDbBlockHeaderDb cid) cdb
Expand All @@ -320,37 +322,35 @@ listenHandler
-> ChainId
-> PactExecutionService
-> MempoolBackend Pact4.UnparsedTransaction
-> Pact4.ListenerRequest
-> Handler Pact4.ListenResponse
listenHandler logger cdb cid pact mem (Pact4.ListenerRequest key) = do
validateRequestKey key

liftIO $ logg Info $ PactCmdLogListen $ Pact4.requestKeyToB16Text key
liftIO (registerDelay defaultTimeout >>= runListen)
-> Pact5.ListenRequest
-> Handler Pact5.ListenResponse
listenHandler logger cdb cid pact mem (Pact5.ListenRequest key) = do
liftIO $ logg Info $ PactCmdLogListen $ Pact5.requestKeyToB64Text key
liftIO (registerDelay defaultTimeout) >>= runListen
where
pdb = view CutDB.cutDbPayloadDb cdb
bdb = fromJuste $ preview (CutDB.cutDbBlockHeaderDb cid) cdb
logg = logFunctionJson (setComponent "listen-handler" logger)
runListen :: TVar Bool -> IO Pact4.ListenResponse
runListen :: TVar Bool -> Handler Pact5.ListenResponse
runListen timedOut = do
startCut <- CutDB._cut cdb
startCut <- liftIO $ CutDB._cut cdb
case HM.lookup cid (_cutMap startCut) of
Nothing -> pure $! Pact4.ListenTimeout defaultTimeout
Nothing -> throwError err504
Just bh -> poll bh
where
go :: BlockHeader -> IO Pact4.ListenResponse
go :: BlockHeader -> Handler Pact5.ListenResponse
go !prevBlock = do
m <- waitForNewBlock prevBlock
m <- liftIO $ waitForNewBlock prevBlock
case m of
Nothing -> pure $! Pact4.ListenTimeout defaultTimeout
Nothing -> throwError err504
Just block -> poll block

poll :: BlockHeader -> IO Pact4.ListenResponse
poll :: BlockHeader -> Handler Pact5.ListenResponse
poll bh = do
hm <- internalPoll logger pdb bdb mem pact Nothing (pure key)
hm <- liftIO $ internalPoll logger pdb bdb mem pact Nothing (pure key)
if HM.null hm
then go bh
else pure $! Pact4.ListenResponse $ snd $ head $ HM.toList hm
else pure $! Pact5.ListenResponse $ snd $ head $ HM.toList hm

waitForNewBlock :: BlockHeader -> IO (Maybe BlockHeader)
waitForNewBlock lastBlockHeader = atomically $ do
Expand Down Expand Up @@ -609,16 +609,16 @@ internalPoll
-> MempoolBackend Pact4.UnparsedTransaction
-> PactExecutionService
-> Maybe ConfirmationDepth
-> NonEmpty Pact4.RequestKey
-> IO (HashMap Pact4.RequestKey (Pact4.CommandResult Pact4.Hash))
-> NonEmpty Pact5.RequestKey
-> IO (HashMap Pact5.RequestKey (Pact5.CommandResult Pact5.Hash (Pact5.PactErrorCompat Pact5.Info)))
internalPoll logger pdb bhdb mempool pactEx confDepth requestKeys0 = do
let dbg txt = logFunctionText logger Debug txt
-- get leaf block header for our chain from current best cut
results0 <- _pactLookup pactEx cid confDepth (coerce requestKeys)
dbg $ "internalPoll.results0: " <> sshow results0
-- TODO: are we sure that all of these are raised locally. This will cause the
-- server to shut down the connection without returning a result to the user.
let results1 = V.map (\rk -> (rk, HM.lookup (coerce $ Pact4.unRequestKey rk) results0)) requestKeysV
let results1 = V.map (\rk -> (rk, HM.lookup (coerce $ Pact5.unRequestKey rk) results0)) requestKeysV
let (present0, missing) = V.unstablePartition (isJust . snd) results1
let present = V.map (second fromJuste) present0
badlisted <- V.toList <$> checkBadList (V.map fst missing)
Expand All @@ -631,19 +631,21 @@ internalPoll logger pdb bhdb mempool pactEx confDepth requestKeys0 = do
where
cid = _chainId bhdb
!requestKeysV = V.fromList $ NEL.toList requestKeys0
!requestKeys = V.map (Pact4.unRequestKey) requestKeysV
!requestKeys = V.map Pact5.unRequestKey requestKeysV

lookup
:: (Pact4.RequestKey, T2 BlockHeight BlockHash)
-> IO (Either String (Maybe (Pact4.RequestKey, Pact4.CommandResult Pact4.Hash)))
:: (Pact5.RequestKey, T2 BlockHeight BlockHash)
-> IO (Either String (Maybe (Pact5.RequestKey, Pact5.CommandResult Pact5.Hash (Pact5.PactErrorCompat Pact5.Info))))
lookup (key, T2 _ ha) = (fmap . fmap . fmap) (key,) $ lookupRequestKey key ha

-- TODO: group by block for performance (not very important right now)
lookupRequestKey :: Pact4.RequestKey -> BlockHash -> IO (Either String (Maybe (Pact4.CommandResult Pact4.Hash)))
lookupRequestKey
:: Pact5.RequestKey
-> BlockHash
-> IO (Either String (Maybe (Pact5.CommandResult Pact5.Hash (Pact5.PactErrorCompat Pact5.Info))))
lookupRequestKey key bHash = runExceptT $ do
let keyHash = Pact4.unRequestKey key
let pactHash = Pact4.fromUntypedHash keyHash
let matchingHash = (== pactHash) . Pact4._cmdHash . fst
let pactHash = Pact5.unRequestKey key
let matchingHash = (== pactHash) . Pact5._cmdHash . fst
blockHeader <- liftIO (TreeDB.lookup bhdb bHash) >>= \case
Nothing -> throwError $ "missing block header: " <> sshow key
Just x -> return x
Expand All @@ -659,37 +661,37 @@ internalPoll logger pdb bhdb mempool pactEx confDepth requestKeys0 = do
Just (_cmd, TransactionOutput output) -> do
out <- case eitherDecodeStrict' output of
Left err -> throwError $
"error decoding tx output for command " <> sshow (Pact4._cmdHash _cmd) <> ": " <> err
Right decodedOutput -> return decodedOutput
when (Pact4._crReqKey out /= key) $
"error decoding tx output for command " <> sshow (Pact5._cmdHash _cmd) <> ": " <> err
Right decodedOutput -> return $ ((fmap . fmap) Pact5._stableEncoding) decodedOutput
when (Pact5._crReqKey out /= key) $
throwError "internal error: Transaction output doesn't match its hash!"
return $ Just $ enrichCR blockHeader out
Nothing -> return Nothing

fromTx :: (Transaction, TransactionOutput) -> ExceptT String IO (Pact4.Command Text, TransactionOutput)
fromTx :: (Transaction, TransactionOutput) -> ExceptT String IO (Pact5.Command Text, TransactionOutput)
fromTx (Transaction txBytes, !out) = do
!tx' <- except $ eitherDecodeStrict' txBytes & _Left %~
(\decodeErr -> "Transaction failed to decode: " <> decodeErr)
return (tx', out)

checkBadList :: Vector Pact4.RequestKey -> IO (Vector (Pact4.RequestKey, Pact4.CommandResult Pact4.Hash))
checkBadList :: Vector Pact5.RequestKey -> IO (Vector (Pact5.RequestKey, Pact5.CommandResult Pact5.Hash (Pact5.PactErrorCompat Pact5.Info)))
checkBadList rkeys = do
let !hashes = V.map pact4RequestKeyToTransactionHash rkeys
let !hashes = V.map pact5RequestKeyToTransactionHash rkeys
out <- mempoolCheckBadList mempool hashes
let bad = V.map (Pact4.RequestKey . Pact4.Hash . unTransactionHash . fst) $
let bad = V.map (Pact5.RequestKey . Pact5.Hash . unTransactionHash . fst) $
V.filter snd $ V.zip hashes out
return $! V.map hashIsOnBadList bad

hashIsOnBadList :: Pact4.RequestKey -> (Pact4.RequestKey, Pact4.CommandResult Pact4.Hash)
hashIsOnBadList :: Pact5.RequestKey -> (Pact5.RequestKey, Pact5.CommandResult Pact5.Hash (Pact5.PactErrorCompat Pact5.Info))
hashIsOnBadList rk =
let res = Pact4.PactResult (Left err)
err = Pact4.PactError Pact4.TxFailure def [] doc
doc = Pact4.pretty (T.pack $ show InsertErrorBadlisted)
!cr = Pact4.CommandResult rk Nothing res 0 Nothing Nothing Nothing []
let res = Pact5.PactResultErr err
err = Pact5.PELegacyError $
Pact5.LegacyPactError Pact5.LegacyTxFailure def [] "This transaction is badlisted because it previously failed to validate."
!cr = Pact5.CommandResult rk Nothing res (mempty :: Pact5.Gas) Nothing Nothing Nothing []
in (rk, cr)

enrichCR :: BlockHeader -> Pact4.CommandResult Pact4.Hash -> Pact4.CommandResult Pact4.Hash
enrichCR bh = set Pact4.crMetaData
enrichCR :: BlockHeader -> Pact5.CommandResult i e -> Pact5.CommandResult i e
enrichCR bh = set Pact5.crMetaData
(Just $ object
[ "blockHeight" .= _blockHeight bh
, "blockTime" .= _blockCreationTime bh
Expand Down
4 changes: 2 additions & 2 deletions src/Chainweb/Pact/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,6 @@ data LocalResult
| LocalPact5PreflightResult (Pact5.CommandResult Pact5.Hash Text) ![Text]
| LocalTimeout
deriving stock (Show, Generic)
deriving anyclass NFData

makePrisms ''LocalResult

Expand Down Expand Up @@ -1299,9 +1298,10 @@ pact5CommandToBytes tx = Transaction
J.encodeStrict tx
}

-- | This function converts CommandResults into bytes in a stable way that can
-- be stored on-chain.
pact5CommandResultToBytes :: Pact5.CommandResult Pact5.Hash Pact5.PactErrorI -> ByteString
pact5CommandResultToBytes cr =
-- TODO: pact5, error codes
J.encodeStrict (fmap convertError cr)
where
convertError err =
Expand Down
Loading

0 comments on commit 3df22fd

Please sign in to comment.