Skip to content

Commit

Permalink
tests
Browse files Browse the repository at this point in the history
  - round trip test for translating exunits to/from plutus
  - determining whether evaluateTransactionExecutionUnits
    provides enough exUnits
  • Loading branch information
redxaxder committed Jul 12, 2021
1 parent b49f598 commit b86a390
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 32 deletions.
12 changes: 2 additions & 10 deletions alonzo/impl/src/Cardano/Ledger/Alonzo/Tools.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import Cardano.Ledger.Alonzo.Scripts
)
import Cardano.Ledger.Alonzo.Tx (DataHash, ScriptPurpose (Spending), rdptr)
import Cardano.Ledger.Alonzo.TxBody (TxOut (..))
import Cardano.Ledger.Alonzo.TxInfo (txInfo, valContext)
import Cardano.Ledger.Alonzo.TxInfo (exBudgetToExUnits, txInfo, valContext)
import Cardano.Ledger.Alonzo.TxWitness (RdmrPtr (..), unRedeemers, unTxDats)
import Cardano.Ledger.BaseTypes (StrictMaybe (..), strictMaybeToMaybe)
import qualified Cardano.Ledger.Core as Core
Expand Down Expand Up @@ -57,20 +57,12 @@ data ScriptFailure c
| -- | The execution budget that was calculated by the Plutus
-- evaluator is out of bounds.
IncompatibleBudget P.ExBudget
deriving (Show)

note :: e -> Maybe a -> Either e a
note _ (Just x) = Right x
note e Nothing = Left e

safeFromInteger :: forall a. (Integral a, Bounded a) => Integer -> Maybe a
safeFromInteger i
| toInteger (minBound :: a) <= i && i <= toInteger (maxBound :: a) = Just $ fromInteger i
| otherwise = Nothing

exBudgetToExUnits :: P.ExBudget -> Maybe ExUnits
exBudgetToExUnits (P.ExBudget (P.ExCPU cpu) (P.ExMemory memory)) =
ExUnits <$> safeFromInteger (toInteger cpu) <*> safeFromInteger (toInteger memory)

-- | Evaluate the execution budgets needed for all the redeemers in
-- a given transaction. If a redeemer is invalid, a failure is returned instead.
--
Expand Down
9 changes: 9 additions & 0 deletions alonzo/impl/src/Cardano/Ledger/Alonzo/TxInfo.hs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,15 @@ transDataPair (x, y) = (transDataHash' x, P.Datum (getPlutusData y))
transExUnits :: ExUnits -> P.ExBudget
transExUnits (ExUnits mem steps) = P.ExBudget (P.ExCPU (fromIntegral steps)) (P.ExMemory (fromIntegral mem))

exBudgetToExUnits :: P.ExBudget -> Maybe ExUnits
exBudgetToExUnits (P.ExBudget (P.ExCPU cpu) (P.ExMemory memory)) =
ExUnits <$> safeFromInteger (toInteger cpu) <*> safeFromInteger (toInteger memory)
where
safeFromInteger :: forall a. (Integral a, Bounded a) => Integer -> Maybe a
safeFromInteger i
| toInteger (minBound :: a) <= i && i <= toInteger (maxBound :: a) = Just $ fromInteger i
| otherwise = Nothing

-- ===================================
-- translate Script Purpose

Expand Down
2 changes: 2 additions & 0 deletions alonzo/test/cardano-ledger-alonzo-test.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,14 @@ test-suite cardano-ledger-alonzo-test
test
other-modules:
Test.Cardano.Ledger.Alonzo.Trials
Test.Cardano.Ledger.Alonzo.Tools
Test.Cardano.Ledger.Alonzo.Golden
Test.Cardano.Ledger.Alonzo.Serialisation.Tripping
Test.Cardano.Ledger.Alonzo.Examples
Test.Cardano.Ledger.Alonzo.Translation
Test.Cardano.Ledger.Alonzo.Serialisation.CDDL
build-depends:
array,
base16-bytestring,
bytestring,
cardano-binary,
Expand Down
32 changes: 10 additions & 22 deletions alonzo/test/test/Test/Cardano/Ledger/Alonzo/Examples.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,10 @@ import Cardano.Ledger.Alonzo.Scripts (Script (..))
import Data.ByteString.Short (ShortByteString)
import Data.Maybe (fromMaybe)
import qualified Plutus.V1.Ledger.Api as P
( EvaluationError (..),
ExBudget (..),
ExCPU (..),
ExMemory (..),
VerboseMode (..),
defaultCostModelParams,
evaluateScriptRestricting,
)
import Plutus.V1.Ledger.Examples
( alwaysFailingNAryFunction,
alwaysSucceedingNAryFunction,
)
import qualified PlutusTx as P
import qualified Test.Cardano.Ledger.Alonzo.PlutusScripts as Generated
( evendata3,
guessTheNumber2,
Expand All @@ -39,27 +30,24 @@ data ShouldSucceed = ShouldSucceed | ShouldFail

directPlutusTest :: ShouldSucceed -> ShortByteString -> [P.Data] -> Assertion
directPlutusTest expectation script ds =
case (expectation, evalWithHugeBudget script ds) of
(ShouldSucceed, (_, Left e)) ->
case (expectation, evalWithTightBudget script ds) of
(ShouldSucceed, Left e) ->
assertBool ("This script should have succeeded, but: " <> show e) False
(ShouldSucceed, (_, Right _)) ->
(ShouldSucceed, Right _) ->
assertBool "" True
(ShouldFail, (_, Left ((P.CekError _)))) ->
(ShouldFail, Left ((P.CekError _))) ->
assertBool "" True -- TODO rule out cost model failure
(ShouldFail, (_, Left e)) ->
(ShouldFail, Left e) ->
assertBool ("Not the script failure we expected: " <> show e) False
(ShouldFail, (_, Right _)) ->
(ShouldFail, Right _) ->
assertBool "This script should have failed" False
where
costModel = fromMaybe (error "corrupt default cost model") P.defaultCostModelParams
-- Evaluate a script with sufficient budget to run it.
evalWithHugeBudget scr datums =
P.evaluateScriptRestricting
P.Verbose
costModel
(P.ExBudget (P.ExCPU 100000000) (P.ExMemory 10000000))
scr
datums
evalWithTightBudget :: ShortByteString -> [P.Data] -> Either P.EvaluationError ()
evalWithTightBudget scr datums = do
budget <- snd $ P.evaluateScriptCounting P.Quiet costModel scr datums
snd $ P.evaluateScriptRestricting P.Verbose costModel budget scr datums

-- | Expects 3 args (data, redeemer, context)
guessTheNumber3 :: ShortByteString
Expand Down
111 changes: 111 additions & 0 deletions alonzo/test/test/Test/Cardano/Ledger/Alonzo/Tools.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{-# Language TypeApplications #-}
{-# Language DataKinds #-}

module Test.Cardano.Ledger.Alonzo.Tools
(tests, testExUnitCalculation)
where

import Test.Tasty (TestTree)
import Test.Tasty.QuickCheck

import Cardano.Ledger.Alonzo.TxInfo (transExUnits, exBudgetToExUnits)

import Cardano.Ledger.Alonzo.Scripts

import Data.Map (Map)
import qualified Data.Map as Map


import Control.State.Transition.Extended (TRC (..))
import Test.Cardano.Ledger.EraBuffet
import Cardano.Ledger.Alonzo
import qualified Cardano.Ledger.Tx as Core

import Shelley.Spec.Ledger.LedgerState (UTxOState (..))
import Shelley.Spec.Ledger.STS.Utxo (UtxoEnv (..))

import Cardano.Ledger.Alonzo.Tx (ValidatedTx (..), IsValidating (..))

import GHC.Records (getField)
import Cardano.Ledger.Alonzo.Rules.Utxos
import Test.Shelley.Spec.Ledger.Utils

import Cardano.Ledger.Alonzo.PParams (PParams' (..))
import Cardano.Slotting.EpochInfo.API
import Cardano.Slotting.Time
import Shelley.Spec.Ledger.UTxO
import Data.Array (Array)

import Cardano.Ledger.Alonzo.Tools
import Cardano.Ledger.Alonzo.Language
import Cardano.Ledger.Alonzo.TxWitness

tests :: TestTree
tests = testProperty "Plutus ExUnit translation round-trip" exUnitsTranslationRoundTrip

-- ExUnits should remain intact when translating to and from the plutus type
exUnitsTranslationRoundTrip :: Gen Property
exUnitsTranslationRoundTrip = do
e <- ExUnits <$> arbitrary <*> arbitrary
pure $ counterexample (show e) $
(exBudgetToExUnits . transExUnits) e == Just e

type A = AlonzoEra StandardCrypto


-- checks plutus script validation against a tx which has had
-- its ex units replaced by the output of evaluateTransactionExecutionUnits
testExUnitCalculation ::
MonadFail m =>
Core.Tx A ->
UTxOState A ->
UtxoEnv A ->
EpochInfo m ->
SystemStart ->
Array Language CostModel ->
m ()
testExUnitCalculation tx utxoState utxoEnv ei ss costmdls = do
tx' <- updateTxExUnits tx utxo ei ss costmdls
_ <- failLeft $ runShelleyBase $
applySTSTest @(UTXOS A) (TRC (utxoEnv, utxoState, vtx tx'))
pure ()
where
utxo = _utxo utxoState

updateTxExUnits ::
MonadFail m =>
Core.Tx A ->
UTxO A ->
EpochInfo m ->
SystemStart ->
Array Language CostModel ->
m (Core.Tx A)
updateTxExUnits tx utxo ei ss costmdls = do
-- rdmrs :: Map RdmrPtr ExUnits
rdmrs <- traverse failLeft =<<
evaluateTransactionExecutionUnits tx utxo ei ss costmdls
pure (replaceRdmrs tx rdmrs)

replaceRdmrs :: Core.Tx A -> Map RdmrPtr ExUnits -> Core.Tx A
replaceRdmrs tx rdmrs = tx { Core.wits = wits' }
where
wits' = (Core.wits tx) { txrdmrs = newrdmrs }
newrdmrs = foldr replaceRdmr (txrdmrs (Core.wits tx)) (Map.assocs rdmrs)

replaceRdmr :: (RdmrPtr,ExUnits) -> Redeemers A -> Redeemers A
replaceRdmr (ptr,ex) x@(Redeemers r) =
case Map.lookup ptr r of
Just (dat,_ex) -> Redeemers $ Map.insert ptr (dat,ex) r
Nothing -> x

failLeft :: MonadFail m => Show e => Either e a -> m a
failLeft (Right a) = pure a
failLeft (Left e) = fail (show e)

vtx :: Core.Tx A -> ValidatedTx A
vtx tx = ValidatedTx
{ body = getField @"body" tx
, wits = getField @"wits" tx
, isValidating = IsValidating True
, auxiliaryData = getField @"auxiliaryData" tx
}
2 changes: 2 additions & 0 deletions alonzo/test/test/Tests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Test.Cardano.Ledger.Alonzo.Examples (plutusScriptExamples)
import Test.Cardano.Ledger.Alonzo.Golden as Golden
import qualified Test.Cardano.Ledger.Alonzo.Serialisation.CDDL as CDDL
import qualified Test.Cardano.Ledger.Alonzo.Serialisation.Tripping as Tripping
import qualified Test.Cardano.Ledger.Alonzo.Tools as TOOLS
import qualified Test.Cardano.Ledger.Alonzo.Translation as Translation
import Test.Cardano.Ledger.Alonzo.Trials (alonzoPropertyTests, fastPropertyTests)
import Test.Tasty
Expand Down Expand Up @@ -44,6 +45,7 @@ fastTests =
[ Tripping.tests,
Translation.tests,
CDDL.tests 1,
TOOLS.tests,
Golden.goldenUTxOEntryMinAda,
plutusScriptExamples
]
Expand Down

0 comments on commit b86a390

Please sign in to comment.