From 3df22fd651982cc647215f333cd4fc89b0a4f23d Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 18 Sep 2024 18:15:37 -0400 Subject: [PATCH] Start using Pact 5 CommandResult for /listen and /poll --- cabal.project | 2 +- chainweb.cabal | 1 + src/Chainweb/Pact/RestAPI.hs | 24 ++--- src/Chainweb/Pact/RestAPI/Client.hs | 37 ++----- src/Chainweb/Pact/RestAPI/Server.hs | 102 +++++++++--------- src/Chainweb/Pact/Types.hs | 4 +- test/Chainweb/Test/Pact4/PactExec.hs | 3 +- test/Chainweb/Test/Pact4/RemotePactTest.hs | 9 +- .../Transaction/Message/Before225.hs | 3 +- test/Chainweb/Test/Pact5/PactServiceTest.hs | 14 ++- test/Chainweb/Test/Pact5/RemotePactTest.hs | 43 +------- .../Test/Pact5/TransactionExecTest.hs | 2 +- test/Chainweb/Test/RestAPI/Utils.hs | 26 +++-- test/Chainweb/Test/Utils.hs | 52 +++++++++ 14 files changed, 166 insertions(+), 156 deletions(-) diff --git a/cabal.project b/cabal.project index f53088bad..9d29ce6c9 100644 --- a/cabal.project +++ b/cabal.project @@ -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 diff --git a/chainweb.cabal b/chainweb.cabal index a0a4754fd..2cda738d2 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -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 diff --git a/src/Chainweb/Pact/RestAPI.hs b/src/Chainweb/Pact/RestAPI.hs index 7ee06d715..16a4ffff3 100644 --- a/src/Chainweb/Pact/RestAPI.hs +++ b/src/Chainweb/Pact/RestAPI.hs @@ -28,8 +28,6 @@ module Chainweb.Pact.RestAPI , pactListenApi , PactSendApi , pactSendApi -, PactPollApi -, pactPollApi , PactLocalWithQueryApi , pactLocalWithQueryApi , PactPollWithQueryApi @@ -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 @@ -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///chain//pact/@ @@ -83,7 +81,7 @@ type PactApi_ = "pact" :> "api" :> "v1" - :> ( ApiSend + :> ( Pact4.ApiSend :<|> PactPollWithQueryApi_ :<|> ApiListen :<|> PactLocalWithQueryApi_ @@ -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) @@ -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 @@ -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_ diff --git a/src/Chainweb/Pact/RestAPI/Client.hs b/src/Chainweb/Pact/RestAPI/Client.hs index fd751e569..e416b0961 100644 --- a/src/Chainweb/Pact/RestAPI/Client.hs +++ b/src/Chainweb/Pact/RestAPI/Client.hs @@ -19,8 +19,6 @@ module Chainweb.Pact.RestAPI.Client , pactSpv2ApiClient , ethSpvApiClient_ , ethSpvApiClient -, pactPollApiClient_ -, pactPollApiClient , pactListenApiClient_ , pactListenApiClient , pactSendApiClient_ @@ -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 @@ -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)) @@ -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 diff --git a/src/Chainweb/Pact/RestAPI/Server.hs b/src/Chainweb/Pact/RestAPI/Server.hs index 9631e95d7..5f3cbeac6 100644 --- a/src/Chainweb/Pact/RestAPI/Server.hs +++ b/src/Chainweb/Pact/RestAPI/Server.hs @@ -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 @@ -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 -- -------------------------------------------------------------------------- -- @@ -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 @@ -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 @@ -609,8 +609,8 @@ 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 @@ -618,7 +618,7 @@ internalPoll logger pdb bhdb mempool pactEx confDepth requestKeys0 = do 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) @@ -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 @@ -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 diff --git a/src/Chainweb/Pact/Types.hs b/src/Chainweb/Pact/Types.hs index 981db1a5a..2ab6bdce2 100644 --- a/src/Chainweb/Pact/Types.hs +++ b/src/Chainweb/Pact/Types.hs @@ -952,7 +952,6 @@ data LocalResult | LocalPact5PreflightResult (Pact5.CommandResult Pact5.Hash Text) ![Text] | LocalTimeout deriving stock (Show, Generic) - deriving anyclass NFData makePrisms ''LocalResult @@ -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 = diff --git a/test/Chainweb/Test/Pact4/PactExec.hs b/test/Chainweb/Test/Pact4/PactExec.hs index cafdb7096..4d515828d 100644 --- a/test/Chainweb/Test/Pact4/PactExec.hs +++ b/test/Chainweb/Test/Pact4/PactExec.hs @@ -22,6 +22,7 @@ module Chainweb.Test.Pact4.PactExec ) where import Control.Lens hiding ((.=)) +import Control.Exception.Safe (tryAny) import Control.Monad import Data.Aeson import qualified Data.ByteString.Lazy.Char8 as BL @@ -568,7 +569,7 @@ execLocalTest runPact name (trans',check) = testCase name (go >>= check) where go = do trans <- trans' - results' <- tryAllSynchronous $ runPact $ + results' <- tryAny $ runPact $ execLocal (Pact4.unparseTransaction trans) Nothing Nothing Nothing case results' of Right (MetadataValidationFailure e) -> diff --git a/test/Chainweb/Test/Pact4/RemotePactTest.hs b/test/Chainweb/Test/Pact4/RemotePactTest.hs index 470007ee7..dc843a69f 100644 --- a/test/Chainweb/Test/Pact4/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact4/RemotePactTest.hs @@ -105,6 +105,7 @@ import Chainweb.Utils hiding (check) import Chainweb.Version import Chainweb.Version.Mainnet import Chainweb.Storage.Table.RocksDB +import qualified Pact.Core.Command.Server as Pact5 -- -------------------------------------------------------------------------- -- -- Global Settings @@ -721,14 +722,14 @@ pollBadKeyTest cenv step = do sid <- liftIO $ mkChainId v maxBound 0 step "RequestKeys of length > 32 fail fast" - runClientM (pactPollApiClient v sid (Poll tooBig)) cenv >>= \case + runClientM (pactPollWithQueryApiClient v sid Nothing (pact4Poll $ Poll tooBig)) cenv >>= \case Left _ -> return () - Right r -> assertFailure $ "Poll succeeded with response: " <> show r + Right (Pact5.PollResponse r) -> assertFailure $ "Poll succeeded with response: " <> show r step "RequestKeys of length < 32 fail fast" - runClientM (pactPollApiClient v sid (Poll tooSmall)) cenv >>= \case + runClientM (pactPollWithQueryApiClient v sid Nothing (pact4Poll $ Poll tooSmall)) cenv >>= \case Left _ -> return () - Right r -> assertFailure $ "Poll succeeded with response: " <> show r + Right (Pact5.PollResponse r) -> assertFailure $ "Poll succeeded with response: " <> show r where toRk = (NEL.:| []) . RequestKey . Hash . SB.toShort diff --git a/test/Chainweb/Test/Pact4/VerifierPluginTest/Transaction/Message/Before225.hs b/test/Chainweb/Test/Pact4/VerifierPluginTest/Transaction/Message/Before225.hs index f31568163..6079e3762 100644 --- a/test/Chainweb/Test/Pact4/VerifierPluginTest/Transaction/Message/Before225.hs +++ b/test/Chainweb/Test/Pact4/VerifierPluginTest/Transaction/Message/Before225.hs @@ -455,6 +455,7 @@ hyperlaneVerifyMerkleNotEnabledFailure = do , checkVerifierNotInTx "hyperlane_v3_message" , PactTxTest (mkMerkleMetadatWithOneSignatureCall hyperlaneMerkleTreeCorrectProof) (\cr -> liftIO $ do - assertTxFailure "should have failed with uncaught exception" "Tx verifier error: Uncaught exception in verifier" cr + -- TODO Pact5: this is a forking change, adding the verifier name to the error + assertTxFailure "should have failed with uncaught exception" "Tx verifier error: Uncaught exception in verifier hyperlane_v3_message" cr assertEqual "gas should have been charged" 20000 (_crGas cr)) ] diff --git a/test/Chainweb/Test/Pact5/PactServiceTest.hs b/test/Chainweb/Test/Pact5/PactServiceTest.hs index 06011bff9..5da4d2a1a 100644 --- a/test/Chainweb/Test/Pact5/PactServiceTest.hs +++ b/test/Chainweb/Test/Pact5/PactServiceTest.hs @@ -68,7 +68,6 @@ import Data.HashMap.Strict qualified as HashMap import Data.HashSet (HashSet) import Data.HashSet qualified as HashSet import Data.Maybe (fromMaybe) -import Data.Text (Text) import Data.Text qualified as T import Data.Text.IO qualified as Text import Data.Vector (Vector) @@ -78,6 +77,7 @@ import Pact.Core.ChainData hiding (ChainId, _chainId) import Pact.Core.Command.Types import Pact.Core.Gas.Types import Pact.Core.Hash qualified as Pact5 +import Pact.Core.StableEncoding qualified as Pact5 import Pact.Core.Names import Pact.Core.PactValue import Pact.Types.Gas qualified as Pact4 @@ -498,7 +498,7 @@ v = pact5InstantCpmTestVersion singletonChainGraph coinModuleName :: ModuleName coinModuleName = ModuleName "coin" Nothing -advanceAllChainsWithTxs :: Fixture -> ChainMap [Pact5.Transaction] -> IO (ChainMap (Vector (CommandResult Pact5.Hash Text))) +advanceAllChainsWithTxs :: Fixture -> ChainMap [Pact5.Transaction] -> IO (ChainMap (Vector TestPact5CommandResult)) advanceAllChainsWithTxs fixture txsPerChain = advanceAllChains fixture $ txsPerChain <&> \txs ph pactQueue mempool -> do @@ -513,7 +513,7 @@ advanceAllChainsWithTxs fixture txsPerChain = advanceAllChains :: () => Fixture -> ChainMap (BlockHeader -> PactQueue -> MempoolBackend Pact4.UnparsedTransaction -> IO PayloadWithOutputs) - -> IO (ChainMap (Vector (CommandResult Pact5.Hash Text))) + -> IO (ChainMap (Vector TestPact5CommandResult)) advanceAllChains Fixture{..} blocks = do commandResults <- forConcurrently (HashSet.toList (chainIds v)) $ \c -> do @@ -538,10 +538,14 @@ advanceAllChains Fixture{..} blocks = do ph' <- getParentTestBlockDb _fixtureBlockDb c payload' <- validateBlock ph' (CheckablePayloadWithOutputs payload) pactQueue assertEqual "payloads must not be altered by validateBlock" payload payload' - commandResults :: Vector (CommandResult Pact5.Hash Text) + commandResults :: Vector TestPact5CommandResult <- forM (_payloadWithOutputsTransactions payload') - (decodeOrThrow' . LBS.fromStrict . _transactionOutputBytes . snd) + ((fmap . fmap . fmap) Pact5._stableEncoding + . decodeOrThrow' + . LBS.fromStrict + . _transactionOutputBytes + . snd) -- assert on the command results return (c, commandResults) diff --git a/test/Chainweb/Test/Pact5/RemotePactTest.hs b/test/Chainweb/Test/Pact5/RemotePactTest.hs index e2acf2500..67e513834 100644 --- a/test/Chainweb/Test/Pact5/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact5/RemotePactTest.hs @@ -29,8 +29,6 @@ module Chainweb.Test.Pact5.RemotePactTest import Chainweb.Test.RestAPI.Utils (getCurrentBlockHeight) import Data.Text qualified as Text import Pact.Core.Errors -import Pact.Core.StableEncoding -import Pact.Core.Info import "pact" Pact.Types.API qualified as Pact4 import "pact" Pact.Types.Command qualified as Pact4 import "pact" Pact.Types.Hash qualified as Pact4 @@ -44,8 +42,7 @@ import Chainweb.Pact.Types import Chainweb.Storage.Table.RocksDB import Chainweb.Test.Pact5.CmdBuilder import Chainweb.Test.TestVersions -import Chainweb.Test.Utils (ChainwebNetwork(..), NodeDbDirs(..), withNodesAtLatestBehavior, withNodeDbDirs, deadbeef) -import Chainweb.Test.Utils (testRetryPolicy) +import Chainweb.Test.Utils import Chainweb.Version import Control.Exception (Exception) import Control.Lens @@ -53,7 +50,6 @@ import Control.Monad.Catch (Handler(..), throwM) import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Resource (ResourceT, runResourceT) import Control.Retry -import Data.Aeson qualified as Aeson import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict qualified as HashMap import Data.List qualified as List @@ -62,13 +58,13 @@ import Data.List.NonEmpty qualified as NE import Data.Text (Text) import Pact.Core.Command.Types import Pact.Core.Hash qualified as Pact5 -import Pact.JSON.Encode qualified as J import PredicateTransformers as PT import Servant.Client import Test.Tasty import Chainweb.Utils import Pact.Core.Gas.Types import Test.Tasty.HUnit (assertBool, assertEqual, assertFailure, testCase) +import qualified Pact.Core.Command.Server as Pact5 {- import Chainweb.Test.Cut.TestBlockDb (TestBlockDb (_bdbPayloadDb, _bdbWebBlockHeaderDb), addTestBlockDb, getCutTestBlockDb, getParentTestBlockDb, mkTestBlockDb, setCutTestBlockDb) import Chainweb.Test.Pact4.Utils (stdoutDummyLogger, testPactServiceConfig, withBlockHeaderDb) @@ -251,13 +247,12 @@ pollingWithDepth :: () -> IO (HashMap RequestKey TestPact5CommandResult) pollingWithDepth clientEnv rks mConfirmationDepth = do recovering testRetryPolicy [retryHandler] $ \_iterNumber -> do - let rksPact4 = NE.map toPact4RequestKey rks - poll <- runClientM (pactPollWithQueryApiClient v cid mConfirmationDepth (Pact4.Poll rksPact4)) clientEnv + poll <- runClientM (pactPollWithQueryApiClient v cid mConfirmationDepth (Pact5.PollRequest rks)) clientEnv case poll of Left e -> do throwM (PollingException (show e)) - Right (Pact4.PollResponses response) -> do - return (convertPollResponse response) + Right (Pact5.PollResponse response) -> do + return response where retryHandler :: RetryStatus -> Handler IO Bool retryHandler _ = Handler $ \case @@ -287,32 +282,6 @@ sending clientEnv cmds = do retryHandler _ = Handler $ \case SendingException _ -> return True -toPact4RequestKey :: RequestKey -> Pact4.RequestKey -toPact4RequestKey = \case - RequestKey (Pact5.Hash bytes) -> Pact4.RequestKey (Pact4.Hash bytes) - -toPact5RequestKey :: Pact4.RequestKey -> RequestKey -toPact5RequestKey = \case - Pact4.RequestKey (Pact4.Hash bytes) -> RequestKey (Pact5.Hash bytes) - -toPact4Command :: Command Text -> Pact4.Command Text -toPact4Command cmd4 = case Aeson.eitherDecodeStrictText (J.encodeText cmd4) of - Left err -> error $ "toPact4Command: decode failed: " ++ err - Right cmd5 -> cmd5 - -toPact5CommandResult :: () - => Pact4.CommandResult Pact4.Hash - -> TestPact5CommandResult -toPact5CommandResult cr4 = case Aeson.eitherDecodeStrictText (J.encodeText cr4) of - Left err -> error $ "toPact5CommandResult: decode failed: " ++ err - Right cr5 -> cr5 - -convertPollResponse :: () - => HashMap Pact4.RequestKey (Pact4.CommandResult Pact4.Hash) - -> HashMap RequestKey TestPact5CommandResult -convertPollResponse pact4Response = HashMap.fromList - $ List.map (\(rk, cr) -> (toPact5RequestKey rk, toPact5CommandResult cr)) - $ HashMap.toList pact4Response trivialTx :: Word -> CmdBuilder trivialTx n = defaultCmd @@ -338,5 +307,3 @@ cid = unsafeChainId 0 v :: ChainwebVersion v = pact5InstantCpmTestVersion singletonChainGraph - -type TestPact5CommandResult = CommandResult Aeson.Value (PactErrorCompat (StableEncoding SpanInfo)) \ No newline at end of file diff --git a/test/Chainweb/Test/Pact5/TransactionExecTest.hs b/test/Chainweb/Test/Pact5/TransactionExecTest.hs index 235b4e23a..c8722c437 100644 --- a/test/Chainweb/Test/Pact5/TransactionExecTest.hs +++ b/test/Chainweb/Test/Pact5/TransactionExecTest.hs @@ -586,7 +586,7 @@ applyCmdVerifierSpec baseRdb = runResourceT $ do (equals [PString "sender00", PString "NoMiner", PDecimal 120318]) (equals coinModuleName) ] - , pt _crResult . traceFailShow . equals $ PactResultOk (PString "Loaded module HcrMpd9fcNbb6fRz07frqtmke2FqRvtXTuV-xTE_NIk") + , pt _crResult . traceFailShow . equals $ PactResultOk (PString "Loaded module free.m, hash HcrMpd9fcNbb6fRz07frqtmke2FqRvtXTuV-xTE_NIk") -- reflects buyGas gas usage, as well as that of the payload , pt _crGas . equals $ Gas 60159 , pt _crContinuation . equals $ Nothing diff --git a/test/Chainweb/Test/RestAPI/Utils.hs b/test/Chainweb/Test/RestAPI/Utils.hs index e5aab2546..9e98c676e 100644 --- a/test/Chainweb/Test/RestAPI/Utils.hs +++ b/test/Chainweb/Test/RestAPI/Utils.hs @@ -81,6 +81,9 @@ import qualified Pact.JSON.Encode as J import Pact.Types.API import Pact.Types.Command import Pact.Types.Hash +import qualified Pact.Core.Command.Server as Pact5 +import qualified Pact.Core.Command.Types as Pact5 +import qualified Pact.Types.API as Pact4 -- ------------------------------------------------------------------ -- -- Defaults @@ -242,6 +245,7 @@ sending v sid cenv batch = -- | Poll with retry using an exponential backoff -- data PollingExpectation = ExpectPactError | ExpectPactResult + deriving Eq polling :: ChainwebVersion @@ -249,7 +253,7 @@ polling -> ClientEnv -> RequestKeys -> PollingExpectation - -> IO PollResponses + -> IO Pact4.PollResponses polling v sid cenv rks pollingExpectation = pollingWithDepth v sid cenv rks Nothing pollingExpectation @@ -260,7 +264,7 @@ pollingWithDepth -> RequestKeys -> Maybe ConfirmationDepth -> PollingExpectation - -> IO PollResponses + -> IO Pact4.PollResponses pollingWithDepth v sid cenv rks confirmationDepth pollingExpectation = recovering testRetryPolicy [h] $ \s -> do debug @@ -271,25 +275,27 @@ pollingWithDepth v sid cenv rks confirmationDepth pollingExpectation = -- by making sure results are successful and request keys -- are sane - runClientM (pactPollWithQueryApiClient v sid confirmationDepth $ Poll rs) cenv >>= \case + runClientM (pactPollWithQueryApiClient v sid confirmationDepth $ Pact5.PollRequest rs) cenv >>= \case Left e -> throwM $ PollingFailure (show e) - Right r@(PollResponses mp) -> + Right r@(Pact5.PollResponse mp) -> if all (go mp) (toList rs) - then return r + then do + let pact4Resps = HM.fromList $ + [ (toPact4RequestKey rk, toPact4CommandResult cr) | (rk, cr) <- HM.toList mp ] + return $ Pact4.PollResponses pact4Resps else throwM $ PollingFailure $ T.unpack $ "polling check failed: " <> J.encodeText r where h _ = Handler $ \case PollingFailure _ -> return True _ -> return False - rs = _rkRequestKeys rks + rs = toPact5RequestKey <$> _rkRequestKeys rks - validate (PactResult a) = case pollingExpectation of - ExpectPactResult -> isRight a - ExpectPactError -> isLeft a + validate Pact5.PactResultOk{} = pollingExpectation == ExpectPactResult + validate Pact5.PactResultErr{} = pollingExpectation == ExpectPactError go m rk = case m ^. at rk of - Just cr -> _crReqKey cr == rk && validate (_crResult cr) + Just cr -> Pact5._crReqKey cr == rk && validate (Pact5._crResult cr) Nothing -> False getCurrentBlockHeight :: ChainwebVersion -> ClientEnv -> ChainId -> IO BlockHeight diff --git a/test/Chainweb/Test/Utils.hs b/test/Chainweb/Test/Utils.hs index 9d987ee43..16f47e8fc 100644 --- a/test/Chainweb/Test/Utils.hs +++ b/test/Chainweb/Test/Utils.hs @@ -29,6 +29,14 @@ module Chainweb.Test.Utils , independentSequentialTestGroup , unsafeHeadOf +, TestPact5CommandResult +, toPact4RequestKey +, toPact5RequestKey +, toPact4Command +, toPact4CommandResult +, toPact5CommandResult +, pact4Poll + -- * Test RocksDb , testRocksDb @@ -237,6 +245,17 @@ import P2P.Peer import Chainweb.Test.Utils.APIValidation import Data.Semigroup +import qualified Pact.Core.Command.Types as Pact5 +import qualified Data.Aeson as Aeson +import qualified Pact.Core.Errors as Pact5 +import qualified Pact.Core.StableEncoding as Pact5 +import qualified Pact.Core.Info as Pact5 +import qualified Pact.Types.Command as Pact4 +import qualified Pact.Core.Hash as Pact5 +import qualified Pact.Types.Hash as Pact4 +import qualified Pact.JSON.Encode as J +import qualified Pact.Types.API as Pact4 +import qualified Pact.Core.Command.Server as Pact5 -- -------------------------------------------------------------------------- -- -- Intialize Test BlockHeader DB @@ -1162,3 +1181,36 @@ independentSequentialTestGroup tn tts = unsafeHeadOf :: HasCallStack => Getting (Endo a) s a -> s -> a unsafeHeadOf l s = s ^?! l + +type TestPact5CommandResult = Pact5.CommandResult Pact5.Hash (Pact5.PactErrorCompat Pact5.SpanInfo) + +toPact4RequestKey :: Pact5.RequestKey -> Pact4.RequestKey +toPact4RequestKey = \case + Pact5.RequestKey (Pact5.Hash bytes) -> Pact4.RequestKey (Pact4.Hash bytes) + +toPact5RequestKey :: Pact4.RequestKey -> Pact5.RequestKey +toPact5RequestKey = \case + Pact4.RequestKey (Pact4.Hash bytes) -> Pact5.RequestKey (Pact5.Hash bytes) + +toPact4Command :: Pact5.Command T.Text -> Pact4.Command T.Text +toPact4Command cmd4 = case Aeson.eitherDecodeStrictText (J.encodeText cmd4) of + Left err -> error $ "toPact4Command: decode failed: " ++ err + Right cmd5 -> cmd5 + +toPact4CommandResult :: () + => TestPact5CommandResult + -> Pact4.CommandResult Pact4.Hash +toPact4CommandResult cr5 = + case Aeson.eitherDecodeStrictText (J.encodeText ((fmap . fmap) Pact5.StableEncoding $ cr5)) of + Left err -> error $ "toPact5CommandResult: decode failed: " ++ err + Right cr4 -> cr4 + +toPact5CommandResult :: () + => Pact4.CommandResult Pact4.Hash + -> TestPact5CommandResult +toPact5CommandResult cr4 = case Aeson.eitherDecodeStrictText (J.encodeText cr4) of + Left err -> error $ "toPact5CommandResult: decode failed: " ++ err + Right cr5 -> (fmap . fmap) Pact5._stableEncoding cr5 + +pact4Poll :: Pact4.Poll -> Pact5.PollRequest +pact4Poll (Pact4.Poll rks) = Pact5.PollRequest $ toPact5RequestKey <$> rks