-
Notifications
You must be signed in to change notification settings - Fork 483
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Data backed constitution script #6784
Merged
Merged
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think validateParamValue can be imported from NonData.Common since it seems to be the same