-
Notifications
You must be signed in to change notification settings - Fork 483
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Data backed constitution script (#6784)
* Add data-backed version of Validator.Common * Add data-backed test modules * Add tests
- Loading branch information
1 parent
38bd591
commit 5266419
Showing
21 changed files
with
14,666 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
cardano-constitution/src/Cardano/Constitution/Data/Validator.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
-- editorconfig-checker-disable-file | ||
{-# LANGUAGE OverloadedLists #-} | ||
module Cardano.Constitution.Data.Validator | ||
( module Export | ||
, defaultValidators | ||
, defaultValidatorsWithCodes | ||
) where | ||
|
||
import Cardano.Constitution.Validator.Data.Common as Export | ||
import Cardano.Constitution.Validator.Data.Sorted qualified as S | ||
import Cardano.Constitution.Validator.Data.Unsorted qualified as U | ||
--import Cardano.Constitution.Validator.Reference.Script qualified as R | ||
|
||
import Data.Map.Strict qualified as M | ||
import PlutusTx.Code | ||
|
||
defaultValidatorsWithCodes :: M.Map String (ConstitutionValidator, CompiledCode ConstitutionValidator) | ||
defaultValidatorsWithCodes = | ||
[ ("sorted", (S.defaultConstitutionValidator, S.defaultConstitutionCode)) | ||
, ("unsorted", (U.defaultConstitutionValidator, U.defaultConstitutionCode)) | ||
-- Disabled, 7 tests fail | ||
-- , ("ref", (R.constitutionScript, R.compiledConstitutionScript)) | ||
] | ||
|
||
defaultValidators :: M.Map String ConstitutionValidator | ||
defaultValidators = fmap fst defaultValidatorsWithCodes |
91 changes: 91 additions & 0 deletions
91
cardano-constitution/src/Cardano/Constitution/Validator/Data/Common.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
-- editorconfig-checker-disable-file | ||
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE NoImplicitPrelude #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE Strict #-} | ||
{-# LANGUAGE TypeApplications #-} | ||
{-# LANGUAGE ViewPatterns #-} | ||
module Cardano.Constitution.Validator.Data.Common | ||
( withChangedParams | ||
, ChangedParams | ||
, ConstitutionValidator | ||
, validateParamValue | ||
) where | ||
|
||
import Cardano.Constitution.Config | ||
import Data.Coerce | ||
import PlutusLedgerApi.Data.V3 | ||
import PlutusTx.Builtins qualified as B | ||
import PlutusTx.Builtins.Internal qualified as BI | ||
import PlutusTx.NonCanonicalRational as NCRatio | ||
import PlutusTx.Prelude as Tx hiding (toList) | ||
|
||
type ConstitutionValidator = ScriptContext -- ^ Deep inside is the changed-parameters proposal | ||
-> BuiltinUnit -- ^ No-error means the proposal conforms to the constitution | ||
|
||
-- OPTIMIZE: operate on BuiltinList<BuiltinPair> directly, needs major refactoring of sorted&unsorted Validators | ||
type ChangedParams = [(BuiltinData, BuiltinData)] | ||
|
||
{- HLINT ignore "Redundant lambda" -} -- I like to see until where it supposed to be first applied. | ||
{- HLINT ignore "Collapse lambdas" -} -- I like to see and comment on each arg | ||
withChangedParams :: (ChangedParams -> Bool) -> ConstitutionValidator | ||
withChangedParams fun (scriptContextToValidGovAction -> validGovAction) = | ||
case validGovAction of | ||
Just cparams -> if fun cparams | ||
then BI.unitval | ||
else traceError "ChangedParams failed to validate" | ||
Nothing -> BI.unitval -- this is a treasury withdrawal, we just accept it | ||
{-# INLINABLE withChangedParams #-} | ||
|
||
validateParamValue :: ParamValue -> BuiltinData -> Bool | ||
validateParamValue = \case | ||
ParamInteger preds -> validatePreds preds . B.unsafeDataAsI | ||
ParamRational preds -> validatePreds preds . coerce . unsafeFromBuiltinData @NonCanonicalRational | ||
ParamList paramValues -> validateParamValues paramValues . BI.unsafeDataAsList | ||
-- accept the actual proposed value without examining it | ||
ParamAny -> const True | ||
where | ||
validateParamValues :: [ParamValue] -> BI.BuiltinList BuiltinData -> Bool | ||
validateParamValues = \case | ||
(paramValueHd : paramValueTl) -> \actualValueData -> | ||
-- if actualValueData is not a cons, it will error | ||
validateParamValue paramValueHd (BI.head actualValueData) | ||
&& validateParamValues paramValueTl (BI.tail actualValueData) | ||
-- if reached the end of list of param-values to check, ensure no more proposed data are left | ||
[] -> B.fromOpaque . BI.null | ||
|
||
validatePreds :: forall a. Tx.Ord a => Predicates a -> a -> Bool | ||
validatePreds (Predicates preds) (validatePred -> validatePredAppliedToActual) = | ||
Tx.all validatePredAppliedToActual preds | ||
|
||
validatePred :: forall a. Tx.Ord a => a -> Predicate a -> Bool | ||
validatePred actualValue (predKey, expectedPredValues) = | ||
Tx.all meaningWithActual expectedPredValues | ||
where | ||
-- we find the meaning (function) from the PredKey | ||
meaning = defaultPredMeanings predKey | ||
-- apply the meaning to actual value: expectedValue is 1st argument, actualValue is 2nd argument | ||
meaningWithActual = (`meaning` actualValue) | ||
{-# INLINABLE validateParamValue #-} | ||
|
||
scriptContextToValidGovAction :: ScriptContext-> Maybe ChangedParams | ||
scriptContextToValidGovAction = | ||
governanceActionToValidGovAction | ||
. ppGovernanceAction | ||
. scriptInfoToProposalProcedure | ||
. scriptContextScriptInfo | ||
|
||
where | ||
scriptInfoToProposalProcedure :: ScriptInfo -> ProposalProcedure | ||
scriptInfoToProposalProcedure si = | ||
case si of | ||
(ProposingScript _ pp) -> pp | ||
_ -> traceError "Not a ProposalProcedure. This should not ever happen, because ledger should guard before, against it." | ||
|
||
governanceActionToValidGovAction :: GovernanceAction -> Maybe ChangedParams | ||
governanceActionToValidGovAction govAction = | ||
case govAction of | ||
(ParameterChange _ cparams _) -> Just . B.unsafeDataAsMap . toBuiltinData $ cparams | ||
(TreasuryWithdrawals _ _) -> Nothing | ||
_ -> traceError "Not a ChangedParams. This should not ever happen, because ledger should guard before, against it." | ||
{-# INLINABLE scriptContextToValidGovAction #-} |
65 changes: 65 additions & 0 deletions
65
cardano-constitution/src/Cardano/Constitution/Validator/Data/Sorted.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
{-# LANGUAGE NoImplicitPrelude #-} | ||
{-# LANGUAGE Strict #-} | ||
{-# LANGUAGE TemplateHaskell #-} | ||
{-# LANGUAGE ViewPatterns #-} | ||
-- Following is for tx compilation | ||
{-# LANGUAGE DataKinds #-} | ||
{-# OPTIONS_GHC -fplugin PlutusTx.Plugin #-} | ||
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:target-version=1.1.0 #-} | ||
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:remove-trace #-} | ||
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:defer-errors #-} | ||
|
||
module Cardano.Constitution.Validator.Data.Sorted | ||
( constitutionValidator | ||
, defaultConstitutionValidator | ||
, mkConstitutionCode | ||
, defaultConstitutionCode | ||
) where | ||
|
||
import Cardano.Constitution.Config | ||
import Cardano.Constitution.Validator.Data.Common as Common | ||
import PlutusCore.Version (plcVersion110) | ||
import PlutusTx as Tx | ||
import PlutusTx.Builtins as B | ||
import PlutusTx.Prelude as Tx | ||
|
||
-- | Expects a constitution-configuration, statically *OR* at runtime via Tx.liftCode | ||
constitutionValidator :: ConstitutionConfig -> ConstitutionValidator | ||
constitutionValidator (ConstitutionConfig cfg) = | ||
Common.withChangedParams (runRules cfg) | ||
|
||
-- | The `runRules` is a loop that works element-wise from left-to-right on the 2 sorted maps. | ||
runRules :: [Param] -- ^ the config (sorted by default) | ||
-> ChangedParams -- ^ the params (came sorted by the ledger) | ||
-> Bool | ||
runRules ((expectedPid, paramValue) : cfgRest) | ||
cparams@((B.unsafeDataAsI -> actualPid, actualValueData) : cparamsRest) = | ||
case actualPid `compare` expectedPid of | ||
EQ -> | ||
Common.validateParamValue paramValue actualValueData | ||
-- drop both heads, and continue checking the next changed param | ||
&& runRules cfgRest cparamsRest | ||
|
||
GT -> -- skip configHead pointing to a parameter not being proposed | ||
runRules cfgRest cparams | ||
LT -> -- actualPid not found in json config, the constitution fails | ||
False | ||
-- if no cparams left: success | ||
-- if cparams left: it means we reached the end of config without validating all cparams | ||
runRules _ cparams = Tx.null cparams | ||
|
||
-- | Statically configure the validator with the `defaultConstitutionConfig`. | ||
defaultConstitutionValidator :: ConstitutionValidator | ||
defaultConstitutionValidator = constitutionValidator defaultConstitutionConfig | ||
|
||
{-| Make a constitution code by supplied the config at runtime. | ||
See Note [Manually constructing a Configuration value] | ||
-} | ||
mkConstitutionCode :: ConstitutionConfig -> CompiledCode ConstitutionValidator | ||
mkConstitutionCode cCfg = $$(compile [|| constitutionValidator ||]) | ||
`unsafeApplyCode` liftCode plcVersion110 cCfg | ||
|
||
-- | The code of the constitution statically configured with the `defaultConstitutionConfig`. | ||
defaultConstitutionCode :: CompiledCode ConstitutionValidator | ||
defaultConstitutionCode = $$(compile [|| defaultConstitutionValidator ||]) |
63 changes: 63 additions & 0 deletions
63
cardano-constitution/src/Cardano/Constitution/Validator/Data/Unsorted.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
{-# LANGUAGE NoImplicitPrelude #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE Strict #-} | ||
{-# LANGUAGE TemplateHaskell #-} | ||
{-# LANGUAGE ViewPatterns #-} | ||
-- Following is for tx compilation | ||
{-# LANGUAGE DataKinds #-} | ||
{-# OPTIONS_GHC -fplugin PlutusTx.Plugin #-} | ||
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:target-version=1.1.0 #-} | ||
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:remove-trace #-} | ||
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:defer-errors #-} | ||
|
||
module Cardano.Constitution.Validator.Data.Unsorted | ||
( constitutionValidator | ||
, defaultConstitutionValidator | ||
, mkConstitutionCode | ||
, defaultConstitutionCode | ||
) where | ||
|
||
import Cardano.Constitution.Config | ||
import Cardano.Constitution.Validator.Data.Common as Common | ||
import PlutusCore.Version (plcVersion110) | ||
import PlutusTx as Tx | ||
import PlutusTx.Builtins as B | ||
import PlutusTx.Prelude as Tx | ||
|
||
-- | Expects a constitution-configuration, statically *OR* at runtime via Tx.liftCode | ||
constitutionValidator :: ConstitutionConfig -> ConstitutionValidator | ||
constitutionValidator cfg = Common.withChangedParams | ||
(all (validateParam cfg)) | ||
|
||
validateParam :: ConstitutionConfig -> (BuiltinData, BuiltinData) -> Bool | ||
validateParam (ConstitutionConfig cfg) (B.unsafeDataAsI -> actualPid, actualValueData) = | ||
Common.validateParamValue | ||
-- If param not found, it will error | ||
(lookupUnsafe actualPid cfg) | ||
actualValueData | ||
|
||
-- | An unsafe version of PlutusTx.AssocMap.lookup, specialised to Integer keys | ||
lookupUnsafe :: Integer -> [(Integer, v)] -> v | ||
lookupUnsafe k = go | ||
where | ||
go [] = traceError "Unsorted lookup failed" | ||
go ((k', i) : xs') = if k `B.equalsInteger` k' | ||
then i | ||
else go xs' | ||
{-# INLINEABLE lookupUnsafe #-} | ||
|
||
-- | Statically configure the validator with the `defaultConstitutionConfig`. | ||
defaultConstitutionValidator :: ConstitutionValidator | ||
defaultConstitutionValidator = constitutionValidator defaultConstitutionConfig | ||
|
||
{-| Make a constitution code by supplied the config at runtime. | ||
See Note [Manually constructing a Configuration value] | ||
-} | ||
mkConstitutionCode :: ConstitutionConfig -> CompiledCode ConstitutionValidator | ||
mkConstitutionCode cCfg = $$(compile [|| constitutionValidator ||]) | ||
`unsafeApplyCode` liftCode plcVersion110 cCfg | ||
|
||
-- | The code of the constitution statically configured with the `defaultConstitutionConfig`. | ||
defaultConstitutionCode :: CompiledCode ConstitutionValidator | ||
defaultConstitutionCode = $$(compile [|| defaultConstitutionValidator ||]) |
93 changes: 93 additions & 0 deletions
93
cardano-constitution/test/Cardano/Constitution/Validator/Data/GoldenTests.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
-- editorconfig-checker-disable-file | ||
module Cardano.Constitution.Validator.Data.GoldenTests | ||
( tests | ||
) where | ||
|
||
import Cardano.Constitution.Config | ||
import Cardano.Constitution.Data.Validator | ||
import Cardano.Constitution.Validator.TestsCommon | ||
import Helpers.TestBuilders | ||
import PlutusCore.Evaluation.Machine.ExBudget | ||
import PlutusCore.Evaluation.Machine.ExBudgetingDefaults | ||
import PlutusCore.Pretty (prettyPlcReadableSimple) | ||
import PlutusLedgerApi.V3 as V3 | ||
import PlutusLedgerApi.V3.ArbitraryContexts as V3 | ||
import PlutusTx.Code as Tx | ||
import UntypedPlutusCore as UPLC | ||
import UntypedPlutusCore.Evaluation.Machine.Cek as UPLC | ||
|
||
import Data.ByteString.Short qualified as SBS | ||
import Data.Map.Strict qualified as M | ||
import Data.Maybe | ||
import Data.String | ||
import System.FilePath | ||
import Test.Tasty | ||
import Test.Tasty.Golden | ||
|
||
import Helpers.Guardrail | ||
|
||
-- The golden files may change, so use `--accept` in cabal `--test-options` to accept the changes **after reviewing them**. | ||
|
||
test_cbor, test_budget_small, test_budget_large, test_readable_pir, test_readable_uplc :: TestTree | ||
|
||
test_cbor = testGroup "Cbor" $ M.elems $ | ||
(\vName (_, vCode) -> | ||
-- The unit of measurement is in bytes | ||
goldenVsString vName (mkPath vName ["cbor","size"]) $ | ||
pure $ fromString $ show $ SBS.length $ V3.serialiseCompiledCode vCode | ||
) `M.mapWithKey` defaultValidatorsWithCodes | ||
|
||
test_budget_large = testGroup "BudgetLarge" $ M.elems $ | ||
(\vName (_, vCode) -> | ||
-- The unit of measurement is in execution steps. | ||
-- See maxTxExSteps, maxTxExMem for limits for chain limits: <https://beta.explorer.cardano.org/en/protocol-parameters/> | ||
goldenVsString vName (mkPath vName ["large","budget"]) $ | ||
pure $ fromString $ show $ runForBudget vCode $ V3.mkFakeParameterChangeContext getFakeLargeParamsChange -- mkLargeFakeProposal defaultConstitutionConfig | ||
)`M.mapWithKey` defaultValidatorsWithCodes | ||
|
||
test_budget_small = testGroup "BudgetSmall" $ M.elems $ | ||
(\vName (_, vCode) -> | ||
-- The unit of measurement is in execution steps. | ||
-- See maxTxExSteps, maxTxExMem for limits for chain limits: <https://beta.explorer.cardano.org/en/protocol-parameters/> | ||
goldenVsString vName (mkPath vName ["small","budget"]) $ | ||
pure $ fromString $ show $ runForBudget vCode $ V3.mkSmallFakeProposal defaultConstitutionConfig | ||
)`M.mapWithKey` defaultValidatorsWithCodes | ||
|
||
test_readable_pir = testGroup "ReadablePir" $ M.elems $ | ||
(\vName (_, vCode) -> | ||
goldenVsString vName (mkPath vName ["pir"]) $ | ||
pure $ fromString $ show $ prettyPlcReadableSimple $ fromJust $ getPirNoAnn vCode | ||
)`M.mapWithKey` defaultValidatorsWithCodes | ||
|
||
test_readable_uplc = testGroup "ReadableUplc" $ M.elems $ | ||
(\vName (_, vCode) -> | ||
goldenVsString vName (mkPath vName ["uplc"]) $ | ||
pure $ fromString $ show $ prettyPlcReadableSimple $ getPlcNoAnn vCode | ||
)`M.mapWithKey` defaultValidatorsWithCodes | ||
|
||
tests :: TestTreeWithTestState | ||
tests = testGroup' "Golden" $ fmap const | ||
[ test_cbor | ||
, test_budget_large | ||
, test_budget_small | ||
, test_readable_pir | ||
, test_readable_uplc | ||
] | ||
|
||
-- HELPERS | ||
|
||
mkPath :: String -> [String] -> FilePath | ||
mkPath vName exts = foldl1 (</>) ["test","Cardano","Constitution","Validator","Data","GoldenTests", foldl (<.>) vName (exts++["golden"])] | ||
|
||
runForBudget :: (ToData ctx) | ||
=> CompiledCode ConstitutionValidator | ||
-> ctx | ||
-> ExBudget | ||
runForBudget v ctx = | ||
let vPs = UPLC._progTerm $ getPlcNoAnn $ v | ||
`unsafeApplyCode` liftCode110 (unsafeFromBuiltinData . toBuiltinData $ ctx) | ||
in case UPLC.runCekDeBruijn defaultCekParametersForTesting counting noEmitter vPs of | ||
-- Here, we guard against the case that a ConstitutionValidator **FAILS EARLY** (for some reason), | ||
-- resulting in misleading low budget costs. | ||
(Left _, _, _) -> error "For safety, we only compare budget of succesful executions." | ||
(Right _ , UPLC.CountingSt budget, _) -> budget |
1 change: 1 addition & 0 deletions
1
...constitution/test/Cardano/Constitution/Validator/Data/GoldenTests/sorted.cbor.size.golden
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
2325 |
1 change: 1 addition & 0 deletions
1
...stitution/test/Cardano/Constitution/Validator/Data/GoldenTests/sorted.large.budget.golden
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ExBudget {exBudgetCPU = ExCPU 610267640, exBudgetMemory = ExMemory 3014102} |
Oops, something went wrong.