From 5ee29364a7a146ee025e94a275bb01360613447f Mon Sep 17 00:00:00 2001 From: Yura Lazarev <1009751+Unisay@users.noreply.github.com> Date: Fri, 17 May 2024 08:23:16 +0200 Subject: [PATCH 01/11] Script to prepare binary executables for uploading to the github release page. (#6015) --- RELEASE.adoc | 16 ++++------------ scripts/prepare-bins.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 12 deletions(-) create mode 100755 scripts/prepare-bins.sh diff --git a/RELEASE.adoc b/RELEASE.adoc index 2bc8e778281..797060bd304 100644 --- a/RELEASE.adoc +++ b/RELEASE.adoc @@ -75,19 +75,11 @@ This updates versions and version bounds, and assembles the changelogs.open a PR - Choose as git tag `x.y.z.0` - Choose as target the git commit hash which points to the release commit - Click `Generate release notes` to automatically fill in the details of what's changed -- Create and attach pir&uplc executables to the release by running the following commands inside the repository where its HEAD is at the release commit: -- -+ -[source,bash] -------------- -nix build .#hydraJobs.x86_64-linux.musl64.ghc96.pir -cp ./result/bin/pir ./pir-x86_64-linux-ghc96 - -nix build .#hydraJobs.x86_64-linux.musl64.ghc96.uplc -cp ./result/bin/uplc ./uplc-x86_64-linux-ghc96 -------------- +- Create and attach pir&uplc executables to the release by running the following script inside the repository where its HEAD is at the release commit: `./scripts/prepare-bins.sh`. This will create `pir-x86_64-linux-ghc96` and `uplc-x86_64-linux-ghc96` executables, compress them and put in the project's root folder. Upload them to the release draft. - Click `Publish release`. -7. Open a PR in the https://github.com/IntersectMBO/cardano-haskell-packages[CHaP repository] for publishing the new version. Run `./scripts/add-from-github.sh "https://github.com/IntersectMBO/plutus" COMMIT-SHA LIST-OF-UPDATED-PACKAGES` (see https://github.com/IntersectMBO/cardano-haskell-packages#-from-github[the README on CHaP]). Example: https://github.com/IntersectMBO/cardano-haskell-packages/pull/394. +7. Open a PR in the https://github.com/IntersectMBO/cardano-haskell-packages[CHaP repository] for publishing the new version. + +If you are making PR from your own fork then don't forget to sync your fork with the upstream first. + +Run `./scripts/add-from-github.sh "https://github.com/IntersectMBO/plutus" COMMIT-SHA LIST-OF-UPDATED-PACKAGES` (see https://github.com/IntersectMBO/cardano-haskell-packages#-from-github[the README on CHaP]). Example: https://github.com/IntersectMBO/cardano-haskell-packages/pull/764. - If issues are found, create a release branch `release/x.y.z`, fix the issues on master, backport the fixes to `release/x.y.z`, tag `x.y.z.0-rc2`, and go to step 4. - Why not just fix the issues on master and tag `x.y.z.0-rc2` from master? It is desirable to minimize the amount of change between `rc1` and `rc2`, because it may reduce the tests and checks that need to be performed against `rc2`. diff --git a/scripts/prepare-bins.sh b/scripts/prepare-bins.sh new file mode 100755 index 00000000000..fada66a996f --- /dev/null +++ b/scripts/prepare-bins.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i bash --pure +#! nix-shell -p bash git nix upx + +set -euo pipefail + +banner='\n +Lets prepare binaries for a release:\n + 1. Build `pir`\n + 2. Compress `pir` with `upx`\n + 3. Build `uplc`\n + 4. Compress `uplc` with `upx`\n +' + +echo -e $banner + +echo "Building pir..." + +nix build ".#hydraJobs.x86_64-linux.musl64.ghc96.pir" + +echo "Compressing pir..." + +upx -9 ./result/bin/pir -o pir-x86_64-linux-ghc96 --force-overwrite + +echo "Building uplc..." + +nix build ".#hydraJobs.x86_64-linux.musl64.ghc96.uplc" + +echo "Compressing uplc..." + +upx -9 ./result/bin/uplc -o uplc-x86_64-linux-ghc96 --force-overwrite From 0d8149c6d45c74d8723cf79961703f4cda341c8f Mon Sep 17 00:00:00 2001 From: Kenneth MacKenzie Date: Mon, 20 May 2024 16:42:07 +0100 Subject: [PATCH 02/11] Kwxm/mainnet script budgets 2 (#6057) * Get the script analysis executable to print out actual execution costs * Tidying up * Empty comment * Dot * Output evaluation status too * Oops --- .../exe/analyse-script-events/Main.hs | 123 ++++++++++++------ 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/plutus-ledger-api/exe/analyse-script-events/Main.hs b/plutus-ledger-api/exe/analyse-script-events/Main.hs index 9b4be82bc36..62a881d0f2c 100644 --- a/plutus-ledger-api/exe/analyse-script-events/Main.hs +++ b/plutus-ledger-api/exe/analyse-script-events/Main.hs @@ -37,7 +37,7 @@ import Data.Primitive.PrimArray qualified as P import Data.SatInt (fromSatInt) import System.Directory.Extra (listFiles) import System.Environment (getArgs, getProgName) -import System.FilePath (isExtensionOf) +import System.FilePath (isExtensionOf, takeFileName) import System.IO (stderr) import Text.Printf (hPrintf, printf) @@ -302,6 +302,70 @@ countBuiltins eventFiles = do P.itraversePrimArray_ printEntry finalCounts where printEntry i c = printf "%-35s %12d\n" (show (toEnum i :: DefaultFun)) c + +data EvaluationResult = OK ExBudget | Failed | DeserialisationError + +-- Convert to a string for use in an R frame +toRString :: EvaluationResult -> String +toRString = \case + OK _ -> "T" + Failed -> "F" + DeserialisationError -> "NA" + +-- Print out the actual and claimed CPU and memory cost of every script. +analyseCosts :: EventAnalyser +analyseCosts ctx _ ev = + case ev of + PlutusV1Event ScriptEvaluationData{..} _ -> + let result = + case deserialiseScript PlutusV1 dataProtocolVersion dataScript of + Left _ -> DeserialisationError + Right script -> + case + V1.evaluateScriptRestricting + dataProtocolVersion + V1.Quiet + ctx + dataBudget + script + dataInputs + of + (_, Left _) -> Failed + (_, Right cost) -> OK cost + in printCost result dataBudget + + PlutusV2Event ScriptEvaluationData{..} _ -> + let result = + case deserialiseScript PlutusV2 dataProtocolVersion dataScript of + Left _ -> DeserialisationError + Right script -> + case + V2.evaluateScriptRestricting + dataProtocolVersion + V2.Quiet + ctx + dataBudget + script + dataInputs + of + (_, Left _) -> Failed + (_, Right cost) -> OK cost + in printCost result dataBudget + + where printCost :: EvaluationResult -> ExBudget -> IO () + printCost result claimedCost = + let (claimedCPU, claimedMem) = costAsInts claimedCost + in case result of + OK cost -> + let (actualCPU, actualMem) = costAsInts cost + in printf "%15d %15d %15d %15d %2s\n" actualCPU claimedCPU actualMem claimedMem (toRString result) + -- Something went wrong; print the cost as "NA" ("Not Available" in R) so that R can + -- still process it. + _ -> + printf "%15s %15d %15s %15d %2s\n" "NA" claimedCPU "NA" claimedMem (toRString result) + costAsInts :: ExBudget -> (Int, Int) + costAsInts (ExBudget (V2.ExCPU cpu) (V2.ExMemory mem)) = (fromSatInt cpu, fromSatInt mem) + -- Extract the script from an evaluation event and apply some analysis function analyseUnappliedScript :: (Term NamedDeBruijn DefaultUni DefaultFun () -> IO ()) @@ -325,6 +389,10 @@ analyseOneFile -> IO () analyseOneFile analyse eventFile = do events <- loadEvents eventFile + printf "# %s\n" $ takeFileName eventFile + -- Print the file in the output so we can narrow down the location of + -- interesting/anomalous data. This may not be helpful for some of the + -- analyses. case ( mkContext V1.mkEvaluationContext (eventsCostParamsV1 events) , mkContext V2.mkEvaluationContext (eventsCostParamsV2 events) ) of @@ -354,29 +422,6 @@ analyseOneFile analyse eventFile = do Nothing -> putStrLn "*** ctxV2 missing ***" -max_tx_ex_steps :: Double -max_tx_ex_steps = 10_000_000_000 - -max_tx_ex_mem :: Double -max_tx_ex_mem = 14_000_000 - --- Print out the CPU and memory budgets of each script event. These are the costs --- paid for by the submitters, not the actual costs consumed during execution. --- TODO: add a version that tells us the actual execution costs. -getBudgets :: EventAnalyser -getBudgets _ctx _params ev = - let printFractions d = - let ExBudget (V2.ExCPU cpu) (V2.ExMemory mem) = dataBudget d - in printf "%15d %10.8f %15d %10.8f\n" - (fromSatInt cpu :: Int) - ((fromSatInt cpu) / max_tx_ex_steps) - (fromSatInt mem :: Int) - ((fromSatInt mem) / max_tx_ex_mem) - - in case ev of - PlutusV1Event evdata _expected -> printFractions evdata - PlutusV2Event evdata _expected -> printFractions evdata - main :: IO () main = let analyses = @@ -400,10 +445,10 @@ main = , "count the total number of occurrences of each builtin in validator scripts" , countBuiltins ) - , ( "budgets" - , "print (claimed) budgets of scripts" - , putStrLn " cpu cpuFraction mem memFraction" - `thenDoAnalysis` getBudgets + , ( "costs" + , "print actual and claimed costs of scripts" + , putStrLn " cpuActual cpuClaimed memActual memClaimed status" + `thenDoAnalysis` analyseCosts ) ] @@ -411,17 +456,21 @@ main = (prelude `thenDoAnalysis` analyser) files = prelude >> doAnalysis analyser files usage = do - getProgName >>= hPrintf stderr "Usage: %s \n" + getProgName >>= hPrintf stderr "Usage: %s []\n" + hPrintf stderr "Analyse the .event files in (default = current directory)\n" hPrintf stderr "Avaliable analyses:\n" mapM_ printDescription analyses where printDescription (n,h,_) = hPrintf stderr " %-16s: %s\n" n h + go name dir = + case find (\(n,_,_) -> n == name) analyses of + Nothing -> printf "Unknown analysis: %s\n" name >> usage + Just (_,_,analysis) -> + filter ("event" `isExtensionOf`) <$> listFiles dir >>= \case + [] -> printf "No .event files in %s\n" dir + eventFiles -> analysis eventFiles + in getArgs >>= \case - [dir, name] -> - case find (\(n,_,_) -> n == name) analyses of - Nothing -> printf "Unknown analysis: %s\n" name >> usage - Just (_,_,analysis) -> - filter ("event" `isExtensionOf`) <$> listFiles dir >>= \case - [] -> printf "No event files in %s\n" dir - eventFiles -> analysis eventFiles - _ -> usage + [name] -> go name "." + [name, dir] -> go name dir + _ -> usage From 1529ef8595aca1cc7c659e2181699272fa9761db Mon Sep 17 00:00:00 2001 From: Ziyang Liu Date: Mon, 20 May 2024 08:59:18 -0700 Subject: [PATCH 03/11] Add integerToByteString and byteStringToInteger to PlutusV2 at PV10 (#6056) --- ...20240517_094957_unsafeFixIO_v2_new_prims.md | 5 +++++ .../PlutusLedgerApi/Common/ProtocolVersions.hs | 18 +++++++++++++++++- .../src/PlutusLedgerApi/Common/Versions.hs | 12 +++++++----- 3 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 plutus-core/changelog.d/20240517_094957_unsafeFixIO_v2_new_prims.md diff --git a/plutus-core/changelog.d/20240517_094957_unsafeFixIO_v2_new_prims.md b/plutus-core/changelog.d/20240517_094957_unsafeFixIO_v2_new_prims.md new file mode 100644 index 00000000000..1cd71254e72 --- /dev/null +++ b/plutus-core/changelog.d/20240517_094957_unsafeFixIO_v2_new_prims.md @@ -0,0 +1,5 @@ + +### Added + +- Primitives `integerToByteString` and `byteStringToInteger` are added to PlutusV2, + enabled at protocol version 10. diff --git a/plutus-ledger-api/src/PlutusLedgerApi/Common/ProtocolVersions.hs b/plutus-ledger-api/src/PlutusLedgerApi/Common/ProtocolVersions.hs index fdf7fbc08a5..158c57fde4a 100644 --- a/plutus-ledger-api/src/PlutusLedgerApi/Common/ProtocolVersions.hs +++ b/plutus-ledger-api/src/PlutusLedgerApi/Common/ProtocolVersions.hs @@ -11,6 +11,7 @@ module PlutusLedgerApi.Common.ProtocolVersions , vasilPV , valentinePV , conwayPV + , conwayPlus1PV , knownPVs , futurePV ) where @@ -68,10 +69,25 @@ valentinePV = MajorProtocolVersion 8 conwayPV :: MajorProtocolVersion conwayPV = MajorProtocolVersion 9 +-- | The next HF after Conway. It doesn't yet have a name, and it's not +-- yet known whether it will be an intra-era HF or introduce a new era. +conwayPlus1PV :: MajorProtocolVersion +conwayPlus1PV = MajorProtocolVersion 10 + -- | The set of protocol versions that are "known", i.e. that have been released -- and have actual differences associated with them. knownPVs :: Set.Set MajorProtocolVersion -knownPVs = Set.fromList [ shelleyPV, allegraPV, maryPV, alonzoPV, vasilPV, valentinePV, conwayPV ] +knownPVs = + Set.fromList + [ shelleyPV + , allegraPV + , maryPV + , alonzoPV + , vasilPV + , valentinePV + , conwayPV + , conwayPlus1PV + ] -- | This is a placeholder for when we don't yet know what protocol version will -- be used for something. It's a very high protocol version that should never diff --git a/plutus-ledger-api/src/PlutusLedgerApi/Common/Versions.hs b/plutus-ledger-api/src/PlutusLedgerApi/Common/Versions.hs index 728b096ea3c..24a553dd651 100644 --- a/plutus-ledger-api/src/PlutusLedgerApi/Common/Versions.hs +++ b/plutus-ledger-api/src/PlutusLedgerApi/Common/Versions.hs @@ -81,7 +81,6 @@ instance Pretty PlutusLedgerLanguage where pretty = viaShow {-| A map indicating which builtin functions were introduced in which 'MajorProtocolVersion'. -Each builtin function should appear at most once. This __must__ be updated when new builtins are added. See Note [New builtins/language versions and protocol versions] @@ -107,6 +106,9 @@ builtinsIntroducedIn = Map.fromList [ ((PlutusV2, valentinePV), Set.fromList [ VerifyEcdsaSecp256k1Signature, VerifySchnorrSecp256k1Signature ]), + ((PlutusV2, conwayPlus1PV), Set.fromList [ + IntegerToByteString, ByteStringToInteger + ]), ((PlutusV3, conwayPV), Set.fromList [ Bls12_381_G1_add, Bls12_381_G1_neg, Bls12_381_G1_scalarMul, Bls12_381_G1_equal, Bls12_381_G1_hashToGroup, @@ -173,10 +175,10 @@ and 'MajorProtocolVersion'? See Note [New builtins/language versions and protocol versions] -} builtinsAvailableIn :: PlutusLedgerLanguage -> MajorProtocolVersion -> Set.Set DefaultFun -builtinsAvailableIn thisLv thisPv = fold $ Map.elems $ - Map.takeWhileAntitone builtinAvailableIn builtinsIntroducedIn +builtinsAvailableIn thisLv thisPv = fold $ + Map.filterWithKey (const . alreadyIntroduced) builtinsIntroducedIn where - builtinAvailableIn :: (PlutusLedgerLanguage, MajorProtocolVersion) -> Bool - builtinAvailableIn (introducedInLv,introducedInPv) = + alreadyIntroduced :: (PlutusLedgerLanguage, MajorProtocolVersion) -> Bool + alreadyIntroduced (introducedInLv,introducedInPv) = -- both should be satisfied introducedInLv <= thisLv && introducedInPv <= thisPv From 9326493dddb036b7e2e8f443112b8e524f3fd361 Mon Sep 17 00:00:00 2001 From: effectfully Date: Mon, 20 May 2024 20:49:16 +0200 Subject: [PATCH 04/11] [Refactoring] Remove 'UnknownBuiltin' and 'UnknownBuiltinType' (#6064) --- .../20240520_192738_effectfully_remove_UnknownBuiltin.md | 3 +++ plutus-core/plutus-core/src/PlutusCore/Error.hs | 5 +---- .../src/PlutusCore/Evaluation/Machine/Exception.hs | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) create mode 100644 plutus-core/changelog.d/20240520_192738_effectfully_remove_UnknownBuiltin.md diff --git a/plutus-core/changelog.d/20240520_192738_effectfully_remove_UnknownBuiltin.md b/plutus-core/changelog.d/20240520_192738_effectfully_remove_UnknownBuiltin.md new file mode 100644 index 00000000000..5fe160f9919 --- /dev/null +++ b/plutus-core/changelog.d/20240520_192738_effectfully_remove_UnknownBuiltin.md @@ -0,0 +1,3 @@ +### Removed + +- `UnknownBuiltin` and `UnknownBuiltinType` in #6064. diff --git a/plutus-core/plutus-core/src/PlutusCore/Error.hs b/plutus-core/plutus-core/src/PlutusCore/Error.hs index 7b4b1e8d42a..7ec30398602 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Error.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Error.hs @@ -55,8 +55,7 @@ throwingEither r e = case e of -- | An error encountered during parsing. data ParserError - = UnknownBuiltinType !T.Text !SourcePos - | BuiltinTypeNotAStar !T.Text !SourcePos + = BuiltinTypeNotAStar !T.Text !SourcePos | UnknownBuiltinFunction !T.Text !SourcePos ![T.Text] | InvalidBuiltinConstant !T.Text !T.Text !SourcePos deriving stock (Eq, Ord, Generic) @@ -171,8 +170,6 @@ instance Pretty SourcePos where pretty = pretty . sourcePosPretty instance Pretty ParserError where - pretty (UnknownBuiltinType s loc) = - "Unknown built-in type" <+> squotes (pretty s) <+> "at" <+> pretty loc pretty (BuiltinTypeNotAStar ty loc) = "Expected a type of kind star (to later parse a constant), but got:" <+> squotes (pretty ty) <+> "at" <+> pretty loc diff --git a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Exception.hs b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Exception.hs index 2ffd78de907..354750cc82c 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Exception.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Exception.hs @@ -60,7 +60,6 @@ data MachineError fun -- ^ A builtin expected a term argument, but something else was received | UnexpectedBuiltinTermArgumentMachineError -- ^ A builtin received a term argument when something else was expected - | UnknownBuiltin fun | NonConstrScrutinized | MissingCaseBranch Word64 deriving stock (Show, Eq, Functor, Generic) @@ -139,8 +138,6 @@ instance (HasPrettyDefaults config ~ 'True, Pretty fun) => "A builtin received a term argument when something else was expected" prettyBy _ (UnliftingMachineError unliftingError) = pretty unliftingError - prettyBy _ (UnknownBuiltin fun) = - "Encountered an unknown built-in function:" <+> pretty fun prettyBy _ NonConstrScrutinized = "A non-constructor value was scrutinized in a case expression" prettyBy _ (MissingCaseBranch i) = From ef9753750109c6a595bc57fd7c8904ece7f3e966 Mon Sep 17 00:00:00 2001 From: zeme-wana <15709674+zeme-wana@users.noreply.github.com> Date: Tue, 21 May 2024 10:35:27 +0200 Subject: [PATCH 05/11] Conditionally add 'triage' label to new issues (#6061) * Add triage label only when the issue does not have any of the 'Internal' labels --- .github/workflows/add-triage-label.yml | 49 ++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/.github/workflows/add-triage-label.yml b/.github/workflows/add-triage-label.yml index 963b4a3ba56..383435d1ce4 100644 --- a/.github/workflows/add-triage-label.yml +++ b/.github/workflows/add-triage-label.yml @@ -1,9 +1,13 @@ +# Whenever a new issue is opened, this workflow adds the "status: needs triage" +# label, unless the issue already has one of the "Internal" labels. + name: Add Triage Label on: issues: types: - reopened - opened + jobs: add-triage-label: runs-on: ubuntu-latest @@ -13,10 +17,41 @@ jobs: - name: Run uses: actions/github-script@v7 with: - script: | - github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ["status: needs triage"] - }) \ No newline at end of file + script: | + const INTERNAL_LABELS = ["Internal", "status: triaged"]; + + async function getIssueLabels() { + const { data: labels } = await github.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + return labels.map(label => label.name); + } + + async function issueHasInternalLabels() { + const labels = await getIssueLabels(); + return INTERNAL_LABELS.some(item => labels.includes(item)); + } + + async function addNeedsTriageLabelToIssue() { + await github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["status: needs triage"] + }); + } + + try { + if (!await issueHasInternalLabels()) { + await addNeedsTriageLabelToIssue(); + } + } catch (error) { + core.setFailed(`Error: ${error}`); + } + + + + + \ No newline at end of file From 0b670f7abb005d4c9475b84975378ac219cce903 Mon Sep 17 00:00:00 2001 From: zeme-wana <15709674+zeme-wana@users.noreply.github.com> Date: Tue, 21 May 2024 10:59:10 +0200 Subject: [PATCH 06/11] Fix bug in add-triage-label.yml (#6067) --- .github/workflows/add-triage-label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/add-triage-label.yml b/.github/workflows/add-triage-label.yml index 383435d1ce4..d896fb0ec22 100644 --- a/.github/workflows/add-triage-label.yml +++ b/.github/workflows/add-triage-label.yml @@ -21,7 +21,7 @@ jobs: const INTERNAL_LABELS = ["Internal", "status: triaged"]; async function getIssueLabels() { - const { data: labels } = await github.issues.listLabelsOnIssue({ + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number From 5eb57a5133db5986f611040feef62a1a79229874 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 12:34:14 +0200 Subject: [PATCH 07/11] chore(deps): bump benchmark-action/github-action-benchmark (#6058) Bumps [benchmark-action/github-action-benchmark](https://github.com/benchmark-action/github-action-benchmark) from 1.19.3 to 1.20.3. - [Release notes](https://github.com/benchmark-action/github-action-benchmark/releases) - [Changelog](https://github.com/benchmark-action/github-action-benchmark/blob/master/CHANGELOG.md) - [Commits](https://github.com/benchmark-action/github-action-benchmark/compare/v1.19.3...v1.20.3) --- updated-dependencies: - dependency-name: benchmark-action/github-action-benchmark dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/longitudinal-benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/longitudinal-benchmark.yml b/.github/workflows/longitudinal-benchmark.yml index 0b704711783..26c74321742 100644 --- a/.github/workflows/longitudinal-benchmark.yml +++ b/.github/workflows/longitudinal-benchmark.yml @@ -41,7 +41,7 @@ jobs: run: git config core.hooksPath no-hooks - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1.19.3 + uses: benchmark-action/github-action-benchmark@v1.20.3 with: name: Plutus Benchmarks tool: 'customSmallerIsBetter' From 0ce328cae98e7dfe9b88c15523e626745ad7aaef Mon Sep 17 00:00:00 2001 From: Ziyang Liu Date: Tue, 21 May 2024 06:52:02 -0700 Subject: [PATCH 08/11] Add integerToByteString and byteStringToInteger to V2.ParamName (#6065) --- plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs | 10 ++++++++++ plutus-ledger-api/test/Spec/CostModelParams.hs | 4 ++-- .../PlutusLedgerApi/Test/V2/EvaluationContext.hs | 2 ++ .../PlutusLedgerApi/Test/V3/EvaluationContext.hs | 2 -- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs b/plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs index 292ea06f506..1f6ef6f5610 100644 --- a/plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs +++ b/plutus-ledger-api/src/PlutusLedgerApi/V2/ParamName.hs @@ -190,5 +190,15 @@ data ParamName = | VerifySchnorrSecp256k1Signature'cpu'arguments'intercept | VerifySchnorrSecp256k1Signature'cpu'arguments'slope | VerifySchnorrSecp256k1Signature'memory'arguments + | IntegerToByteString'cpu'arguments'c0 + | IntegerToByteString'cpu'arguments'c1 + | IntegerToByteString'cpu'arguments'c2 + | IntegerToByteString'memory'arguments'intercept + | IntegerToByteString'memory'arguments'slope + | ByteStringToInteger'cpu'arguments'c0 + | ByteStringToInteger'cpu'arguments'c1 + | ByteStringToInteger'cpu'arguments'c2 + | ByteStringToInteger'memory'arguments'intercept + | ByteStringToInteger'memory'arguments'slope deriving stock (Eq, Ord, Enum, Ix, Bounded, Generic) deriving IsParamName via (GenericParamName ParamName) diff --git a/plutus-ledger-api/test/Spec/CostModelParams.hs b/plutus-ledger-api/test/Spec/CostModelParams.hs index 6cff19826ac..3edb933edc4 100644 --- a/plutus-ledger-api/test/Spec/CostModelParams.hs +++ b/plutus-ledger-api/test/Spec/CostModelParams.hs @@ -30,8 +30,8 @@ tests = [ testCase "length" $ do 166 @=? length v1_ParamNames 166 @=? length V1.costModelParamsForTesting - 175 @=? length v2_ParamNames - 175 @=? length V2.costModelParamsForTesting + 185 @=? length v2_ParamNames + 185 @=? length V2.costModelParamsForTesting 233 @=? length v3_ParamNames 233 @=? length V3.costModelParamsForTesting , testCase "tripping paramname" $ do diff --git a/plutus-ledger-api/testlib/PlutusLedgerApi/Test/V2/EvaluationContext.hs b/plutus-ledger-api/testlib/PlutusLedgerApi/Test/V2/EvaluationContext.hs index a95463039cd..e7bbec6cd2d 100644 --- a/plutus-ledger-api/testlib/PlutusLedgerApi/Test/V2/EvaluationContext.hs +++ b/plutus-ledger-api/testlib/PlutusLedgerApi/Test/V2/EvaluationContext.hs @@ -52,4 +52,6 @@ clearBuiltinCostModel r = r { paramSerialiseData = mempty , paramVerifyEcdsaSecp256k1Signature = mempty , paramVerifySchnorrSecp256k1Signature = mempty + , paramIntegerToByteString = mempty + , paramByteStringToInteger = mempty } diff --git a/plutus-ledger-api/testlib/PlutusLedgerApi/Test/V3/EvaluationContext.hs b/plutus-ledger-api/testlib/PlutusLedgerApi/Test/V3/EvaluationContext.hs index e4e7e74c3ed..62ae7ccab21 100644 --- a/plutus-ledger-api/testlib/PlutusLedgerApi/Test/V3/EvaluationContext.hs +++ b/plutus-ledger-api/testlib/PlutusLedgerApi/Test/V3/EvaluationContext.hs @@ -67,6 +67,4 @@ clearBuiltinCostModel r = r , paramBls12_381_finalVerify = mempty , paramKeccak_256 = mempty , paramBlake2b_224 = mempty - , paramIntegerToByteString = mempty - , paramByteStringToInteger = mempty } From acdaeb24b1257ab9c5d2de1a10a8af24a5d75039 Mon Sep 17 00:00:00 2001 From: Ana Pantilie <45069775+ana-pantilie@users.noreply.github.com> Date: Tue, 21 May 2024 20:15:55 +0300 Subject: [PATCH 09/11] Add PlutusTx Map backed by Data (#5927) Co-authored-by: Ziyang Liu Signed-off-by: Ana Pantilie --- plutus-tx-plugin/plutus-tx-plugin.cabal | 3 + plutus-tx-plugin/test/AssocMap/Spec.hs | 806 ++++++++++++++++++ .../test/Budget/9.6/map1-budget.budget.golden | 2 + .../test/Budget/9.6/map1.eval.golden | 8 + .../test/Budget/9.6/map1.pir.golden | 404 +++++++++ .../test/Budget/9.6/map1.uplc.golden | 412 +++++++++ .../test/Budget/9.6/map2-budget.budget.golden | 2 + .../test/Budget/9.6/map2.eval.golden | 27 + .../test/Budget/9.6/map2.pir.golden | 338 ++++++++ .../test/Budget/9.6/map2.uplc.golden | 272 ++++++ .../test/Budget/9.6/map3-budget.budget.golden | 2 + .../test/Budget/9.6/map3.eval.golden | 27 + .../test/Budget/9.6/map3.pir.golden | 338 ++++++++ .../test/Budget/9.6/map3.uplc.golden | 272 ++++++ plutus-tx-plugin/test/Budget/Spec.hs | 2 +- plutus-tx-plugin/test/Spec.hs | 4 +- plutus-tx-plugin/test/Util/Common.hs | 88 ++ ...16_215552_ana.pantilie95_data_assoclist.md | 7 + plutus-tx/plutus-tx.cabal | 1 + plutus-tx/src/PlutusTx/AssocMap.hs | 2 +- plutus-tx/src/PlutusTx/Builtins.hs | 5 + plutus-tx/src/PlutusTx/Data/AssocMap.hs | 412 +++++++++ plutus-tx/src/PlutusTx/Eq.hs | 8 + plutus-tx/src/PlutusTx/IsData/Instances.hs | 2 + plutus-tx/src/PlutusTx/Lift/Instances.hs | 2 + plutus-tx/src/PlutusTx/Ord.hs | 15 + plutus-tx/src/PlutusTx/Show.hs | 2 + plutus-tx/src/PlutusTx/These.hs | 5 +- 28 files changed, 3464 insertions(+), 4 deletions(-) create mode 100644 plutus-tx-plugin/test/AssocMap/Spec.hs create mode 100644 plutus-tx-plugin/test/Budget/9.6/map1-budget.budget.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map1.eval.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map1.pir.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map1.uplc.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map2-budget.budget.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map2.eval.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map2.pir.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map2.uplc.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map3-budget.budget.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map3.eval.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map3.pir.golden create mode 100644 plutus-tx-plugin/test/Budget/9.6/map3.uplc.golden create mode 100644 plutus-tx-plugin/test/Util/Common.hs create mode 100644 plutus-tx/changelog.d/20240516_215552_ana.pantilie95_data_assoclist.md create mode 100644 plutus-tx/src/PlutusTx/Data/AssocMap.hs diff --git a/plutus-tx-plugin/plutus-tx-plugin.cabal b/plutus-tx-plugin/plutus-tx-plugin.cabal index 2ebff4e86e2..bdf5c1a1482 100644 --- a/plutus-tx-plugin/plutus-tx-plugin.cabal +++ b/plutus-tx-plugin/plutus-tx-plugin.cabal @@ -128,6 +128,7 @@ test-suite plutus-tx-plugin-tests other-modules: AsData.Budget.Spec AsData.Budget.Types + AssocMap.Spec Blueprint.Tests Blueprint.Tests.Lib Blueprint.Tests.Lib.AsData.Blueprint @@ -171,6 +172,7 @@ test-suite plutus-tx-plugin-tests TH.Spec TH.TestTH Unicode.Spec + Util.Common build-depends: , base >=4.9 && <5 @@ -192,6 +194,7 @@ test-suite plutus-tx-plugin-tests , tasty-hunit , template-haskell , text + , these default-extensions: Strict ghc-options: -threaded -rtsopts -with-rtsopts=-N diff --git a/plutus-tx-plugin/test/AssocMap/Spec.hs b/plutus-tx-plugin/test/AssocMap/Spec.hs new file mode 100644 index 00000000000..d8658ebd69c --- /dev/null +++ b/plutus-tx-plugin/test/AssocMap/Spec.hs @@ -0,0 +1,806 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NegativeLiterals #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE ViewPatterns #-} +{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:context-level=0 #-} +{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:defer-errors #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MonoLocalBinds #-} + +module AssocMap.Spec where + +import Test.Tasty.Extras + +import Data.List (nubBy, sort) +import Data.Map.Strict qualified as Map +import Data.These qualified as Haskell +import Hedgehog (Gen, MonadTest, Property, Range, forAll, property, (===)) +import Hedgehog.Gen qualified as Gen +import Hedgehog.Range qualified as Range +import PlutusTx.AssocMap qualified as AssocMap +import PlutusTx.Builtins qualified as PlutusTx +import PlutusTx.Code +import PlutusTx.Data.AssocMap qualified as Data.AssocMap +import PlutusTx.IsData () +import PlutusTx.IsData qualified as P +import PlutusTx.Lift (liftCodeDef, makeLift) +import PlutusTx.List qualified as PlutusTx +import PlutusTx.Prelude qualified as PlutusTx +import PlutusTx.Show qualified as PlutusTx +import PlutusTx.Test +import PlutusTx.TH (compile) +import PlutusTx.These (These (..), these) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.Hedgehog (testProperty) +import Util.Common (cekResultMatchesHaskellValue, compiledCodeToTerm, unsafeRunTermCek) + + +-- | Test the performance and interaction between 'insert', 'delete' and 'lookup'. +map1 :: + CompiledCode + ( Integer -> + ( Maybe PlutusTx.BuiltinByteString + , Maybe PlutusTx.BuiltinByteString + , Maybe PlutusTx.BuiltinByteString + , Maybe PlutusTx.BuiltinByteString + , Maybe PlutusTx.BuiltinByteString + ) + ) +map1 = + $$( compile + [|| + \n -> + let m :: Data.AssocMap.Map Integer PlutusTx.BuiltinByteString + m = + foldr + (\i -> + Data.AssocMap.insert + (n PlutusTx.+ i) + (PlutusTx.encodeUtf8 (PlutusTx.show i)) + ) + (Data.AssocMap.singleton n "0") + (PlutusTx.enumFromTo 1 10) + m' = Data.AssocMap.delete (n PlutusTx.+ 5) m + in ( Data.AssocMap.lookup n m + , Data.AssocMap.lookup (n PlutusTx.+ 5) m + , Data.AssocMap.lookup (n PlutusTx.+ 10) m + , Data.AssocMap.lookup (n PlutusTx.+ 20) m + , Data.AssocMap.lookup (n PlutusTx.+ 5) m' + ) + ||] + ) + +-- | Test that 'unionWith' is implemented correctly. Due to the nature of 'Map k v', +-- some type errors are only caught when running the PlutusTx compiler on code which uses +-- 'unionWith'. +map2 :: CompiledCode (Integer -> [(Integer, PlutusTx.BuiltinString)]) +map2 = + $$( compile + [|| + \n -> + let m1 = + Data.AssocMap.unsafeFromList + [ (n PlutusTx.+ 1, "one") + , (n PlutusTx.+ 2, "two") + , (n PlutusTx.+ 3, "three") + , (n PlutusTx.+ 4, "four") + , (n PlutusTx.+ 5, "five") + ] + m2 = + Data.AssocMap.unsafeFromList + [ (n PlutusTx.+ 3, "THREE") + , (n PlutusTx.+ 4, "FOUR") + , (n PlutusTx.+ 6, "SIX") + , (n PlutusTx.+ 7, "SEVEN") + ] + m = Data.AssocMap.unionWith PlutusTx.appendByteString m1 m2 + in PlutusTx.fmap (\(k, v) -> (k, PlutusTx.decodeUtf8 v)) (Data.AssocMap.toList m) + ||] + ) + +-- | Similar to map2, but uses 'union' instead of 'unionWith'. Evaluating 'map3' and 'map2' +-- should yield the same result. +map3 :: CompiledCode (Integer -> [(Integer, PlutusTx.BuiltinString)]) +map3 = + $$( compile + [|| + \n -> + let m1 = + Data.AssocMap.unsafeFromList + [ (n PlutusTx.+ 1, "one") + , (n PlutusTx.+ 2, "two") + , (n PlutusTx.+ 3, "three") + , (n PlutusTx.+ 4, "four") + , (n PlutusTx.+ 5, "five") + ] + m2 = + Data.AssocMap.unsafeFromList + [ (n PlutusTx.+ 3, "THREE") + , (n PlutusTx.+ 4, "FOUR") + , (n PlutusTx.+ 6, "SIX") + , (n PlutusTx.+ 7, "SEVEN") + ] + m = Data.AssocMap.union m1 m2 + f = these id id PlutusTx.appendByteString + in PlutusTx.fmap (\(k, v) -> (k, PlutusTx.decodeUtf8 (f v))) (Data.AssocMap.toList m) + ||] + ) + +lookupProgram :: CompiledCode (Integer -> AssocMap.Map Integer Integer -> Maybe Integer) +lookupProgram = $$(compile [|| AssocMap.lookup ||]) + +dataLookupProgram :: CompiledCode (Integer -> Data.AssocMap.Map Integer Integer -> Maybe Integer) +dataLookupProgram = $$(compile [|| Data.AssocMap.lookup ||]) + +memberProgram :: CompiledCode (Integer -> AssocMap.Map Integer Integer -> Bool) +memberProgram = $$(compile [|| AssocMap.member ||]) + +dataMemberProgram :: CompiledCode (Integer -> Data.AssocMap.Map Integer Integer -> Bool) +dataMemberProgram = $$(compile [|| Data.AssocMap.member ||]) + +insertProgram + :: CompiledCode + ( Integer + -> Integer + -> AssocMap.Map Integer Integer + -> [(Integer, Integer)] + ) +insertProgram = + $$(compile + [|| \k v m -> + PlutusTx.sort $ AssocMap.toList $ AssocMap.insert k v m + ||]) + +dataInsertProgram + :: CompiledCode + ( Integer + -> Integer + -> Data.AssocMap.Map Integer Integer + -> [(Integer, Integer)] + ) +dataInsertProgram = + $$(compile + [|| \k v m -> + PlutusTx.sort $ Data.AssocMap.toList $ Data.AssocMap.insert k v m + ||]) + +deleteProgram + :: CompiledCode + ( Integer + -> AssocMap.Map Integer Integer + -> [(Integer, Integer)] + ) +deleteProgram = + $$(compile + [|| \k m -> + PlutusTx.sort $ AssocMap.toList $ AssocMap.delete k m + ||]) + +dataDeleteProgram + :: CompiledCode + ( Integer + -> Data.AssocMap.Map Integer Integer + -> [(Integer, Integer)] + ) +dataDeleteProgram = + $$(compile + [|| \k m -> + PlutusTx.sort $ Data.AssocMap.toList $ Data.AssocMap.delete k m + ||]) + +allProgram + :: CompiledCode + ( Integer + -> AssocMap.Map Integer Integer + -> Bool + ) +allProgram = + $$(compile [|| \num m -> AssocMap.all (\x -> x PlutusTx.< num) m ||]) + +dataAllProgram + :: CompiledCode + ( Integer + -> Data.AssocMap.Map Integer Integer + -> Bool + ) +dataAllProgram = + $$(compile [|| \num m -> Data.AssocMap.all (\x -> x PlutusTx.< num) m ||]) + +dataAnyProgram + :: CompiledCode + ( Integer + -> Data.AssocMap.Map Integer Integer + -> Bool + ) +dataAnyProgram = + $$(compile [|| \num m -> Data.AssocMap.any (\x -> x PlutusTx.< num) m ||]) + +keysProgram + :: CompiledCode + ( AssocMap.Map Integer Integer + -> [Integer] + ) +keysProgram = + $$(compile [|| AssocMap.keys ||]) + +dataNoDuplicateKeysProgram + :: CompiledCode + ( Data.AssocMap.Map Integer Integer + -> Bool + ) +dataNoDuplicateKeysProgram = + $$(compile [|| Data.AssocMap.noDuplicateKeys ||]) + +unionProgram + :: CompiledCode + ( AssocMap.Map Integer Integer + -> AssocMap.Map Integer Integer + -> [(Integer, These Integer Integer)] + ) +unionProgram = + $$(compile + [|| \m1 m2 -> + PlutusTx.sort $ AssocMap.toList $ AssocMap.union m1 m2 + ||]) + +dataUnionProgram + :: CompiledCode + ( Data.AssocMap.Map Integer Integer + -> Data.AssocMap.Map Integer Integer + -> [(Integer, These Integer Integer)] + ) +dataUnionProgram = + $$(compile + [|| \m1 m2 -> + PlutusTx.sort $ Data.AssocMap.toList $ Data.AssocMap.union m1 m2 + ||]) + +unionWithProgram + :: CompiledCode + ( AssocMap.Map Integer Integer + -> AssocMap.Map Integer Integer + -> [(Integer, Integer)] + ) +unionWithProgram = + $$(compile + [|| \m1 m2 -> + PlutusTx.sort $ AssocMap.toList $ AssocMap.unionWith (\x _ -> x) m1 m2 + ||]) + +dataUnionWithProgram + :: CompiledCode + ( Data.AssocMap.Map Integer Integer + -> Data.AssocMap.Map Integer Integer + -> [(Integer, Integer)] + ) +dataUnionWithProgram = + $$(compile + [|| \m1 m2 -> + PlutusTx.sort $ Data.AssocMap.toList $ Data.AssocMap.unionWith (\x _ -> x) m1 m2 + ||]) + +encodedDataAssocMap + :: CompiledCode + ( Data.AssocMap.Map Integer Integer + -> PlutusTx.BuiltinData + ) +encodedDataAssocMap = $$(compile [|| P.toBuiltinData ||]) + +encodedAssocMap + :: CompiledCode + ( AssocMap.Map Integer Integer + -> PlutusTx.BuiltinData + ) +encodedAssocMap = $$(compile [|| P.toBuiltinData ||]) + +mDecodedDataAssocMap + :: CompiledCode + ( Data.AssocMap.Map Integer Integer + -> PlutusTx.Maybe [(Integer, Integer)] + ) +mDecodedDataAssocMap = + $$(compile + [|| fmap (PlutusTx.sort . Data.AssocMap.toList) . P.fromBuiltinData . P.toBuiltinData + ||]) + +mDecodedAssocMap + :: CompiledCode + ( AssocMap.Map Integer Integer + -> PlutusTx.Maybe [(Integer, Integer)] + ) +mDecodedAssocMap = + $$(compile + [|| fmap (PlutusTx.sort . AssocMap.toList) . P.fromBuiltinData . P.toBuiltinData + ||]) + +decodedDataAssocMap + :: CompiledCode + ( Data.AssocMap.Map Integer Integer + -> [(Integer, Integer)] + ) +decodedDataAssocMap = + $$(compile + [|| PlutusTx.sort . Data.AssocMap.toList . P.unsafeFromBuiltinData . P.toBuiltinData + ||]) + +decodedAssocMap + :: CompiledCode + ( AssocMap.Map Integer Integer + -> [(Integer, Integer)] + ) +decodedAssocMap = + $$(compile + [|| PlutusTx.sort . AssocMap.toList . P.unsafeFromBuiltinData . P.toBuiltinData + ||]) + +-- | The semantics of PlutusTx maps and their operations. +-- The 'PlutusTx' implementations maps ('Data.AssocMap.Map' and 'AssocMap.Map') +-- are checked against the semantics to ensure correctness. +newtype AssocMapS k v = AssocMapS [(k, v)] + deriving stock (Show, Eq) + +semanticsToAssocMap :: AssocMapS k v -> AssocMap.Map k v +semanticsToAssocMap = AssocMap.unsafeFromList . toListS + +semanticsToDataAssocMap + :: (P.ToData k, P.ToData v) + => AssocMapS k v -> Data.AssocMap.Map k v +semanticsToDataAssocMap = Data.AssocMap.unsafeFromList . toListS + +assocMapToSemantics :: AssocMap.Map k v -> AssocMapS k v +assocMapToSemantics = unsafeFromListS . AssocMap.toList + +dataAssocMapToSemantics + :: (P.UnsafeFromData k, P.UnsafeFromData v) + => Data.AssocMap.Map k v -> AssocMapS k v +dataAssocMapToSemantics = unsafeFromListS . Data.AssocMap.toList + +nullS :: AssocMapS k v -> Bool +nullS (AssocMapS l) = null l + +sortS :: (Ord k, Ord v) => AssocMapS k v -> AssocMapS k v +sortS (AssocMapS l) = AssocMapS $ sort l + +toListS :: AssocMapS k v -> [(k, v)] +toListS (AssocMapS l) = l + +unsafeFromListS :: [(k, v)] -> AssocMapS k v +unsafeFromListS = AssocMapS + +safeFromListS :: Ord k => [(k, v)] -> AssocMapS k v +safeFromListS = AssocMapS . Map.toList . Map.fromList + +lookupS :: Integer -> AssocMapS Integer Integer -> Maybe Integer +lookupS k (AssocMapS l) = Map.lookup k . Map.fromList $ l + +memberS :: Integer -> AssocMapS Integer Integer -> Bool +memberS k (AssocMapS l) = Map.member k . Map.fromList $ l + +insertS :: Integer -> Integer -> AssocMapS Integer Integer -> AssocMapS Integer Integer +insertS k v (AssocMapS l) = + AssocMapS . Map.toList . Map.insert k v . Map.fromList $ l + +deleteS :: Integer -> AssocMapS Integer Integer -> AssocMapS Integer Integer +deleteS k (AssocMapS l) = + AssocMapS . Map.toList . Map.delete k . Map.fromList $ l + +allS :: (Integer -> Bool) -> AssocMapS Integer Integer -> Bool +allS p (AssocMapS l) = all (p . snd) l + +anyS :: (Integer -> Bool) -> AssocMapS Integer Integer -> Bool +anyS p (AssocMapS l) = any (p . snd) l + +keysS :: AssocMapS Integer Integer -> [Integer] +keysS (AssocMapS l) = map fst l + +noDuplicateKeysS :: AssocMapS Integer Integer -> Bool +noDuplicateKeysS (AssocMapS l) = + length l == length (nubBy (\(k1, _) (k2, _) -> k1 == k2) l) + +mapS :: (a -> b) -> AssocMapS k a -> AssocMapS k b +mapS f (AssocMapS l) = AssocMapS $ map (\(k, v) -> (k, f v)) l + +makeLift ''AssocMapS + +-- | The semantics of 'union' is based on the 'AssocMap' implementation. +-- The code is duplicated here to avoid any issues if the 'AssocMap' implementation changes. +unionS + :: AssocMapS Integer Integer + -> AssocMapS Integer Integer + -> AssocMapS Integer (Haskell.These Integer Integer) +unionS (AssocMapS ls) (AssocMapS rs) = + let + f a b' = case b' of + Nothing -> Haskell.This a + Just b -> Haskell.These a b + + ls' = fmap (\(c, i) -> (c, f i (lookupS c (AssocMapS rs)))) ls + + -- Keeps only those keys which don't appear in the left map. + rs' = filter (\(c, _) -> not (any (\(c', _) -> c' == c) ls)) rs + + rs'' = fmap (fmap Haskell.That) rs' + in + AssocMapS (ls' ++ rs'') + +haskellToPlutusThese :: Haskell.These a b -> These a b +haskellToPlutusThese = \case + Haskell.This a -> This a + Haskell.That b -> That b + Haskell.These a b -> These a b + +unionWithS + :: (Integer -> Integer -> Integer) + -> AssocMapS Integer Integer + -> AssocMapS Integer Integer + -> AssocMapS Integer Integer +unionWithS merge (AssocMapS ls) (AssocMapS rs) = + AssocMapS + . Map.toList + $ Map.unionWith merge (Map.fromList ls) (Map.fromList rs) + +genAssocMapS :: Gen (AssocMapS Integer Integer) +genAssocMapS = + AssocMapS . Map.toList <$> Gen.map rangeLength genPair + where + genPair :: Gen (Integer, Integer) + genPair = do + (,) <$> Gen.integral rangeElem <*> Gen.integral rangeElem + +genUnsafeAssocMapS :: Gen (AssocMapS Integer Integer) +genUnsafeAssocMapS = do + AssocMapS <$> Gen.list rangeLength genPair + where + genPair :: Gen (Integer, Integer) + genPair = do + (,) <$> Gen.integral rangeElem <*> Gen.integral rangeElem + +-- | The 'Equivalence' class is used to define an equivalence relation +-- between `AssocMapS` and the 'PlutusTx' implementations. +class Equivalence l where + (~~) :: + ( MonadTest m + , Show k + , Show v + , Ord k + , Ord v + , P.UnsafeFromData k + , P.UnsafeFromData v + ) => AssocMapS k v -> l k v -> m () + +-- | An `AssocMap.Map` is equivalent to an `AssocMapS` if they have the same elements. +instance Equivalence AssocMap.Map where + assocMapS ~~ assocMap = + sortS assocMapS === sortS (assocMapToSemantics assocMap) + +-- | An `Data.AssocMap.Map` is equivalent to an `AssocMapS` if they have the same elements. +instance Equivalence Data.AssocMap.Map where + assocMapS ~~ dataAssocMap = + sortS assocMapS === sortS (dataAssocMapToSemantics dataAssocMap) + +rangeElem :: Range Integer +rangeElem = Range.linear 0 100 + +rangeLength :: Range Int +rangeLength = Range.linear 0 100 + +safeFromListSpec :: Property +safeFromListSpec = property $ do + assocMapS <- forAll genAssocMapS + let assocMap = AssocMap.safeFromList . toListS $ assocMapS + dataAssocMap = Data.AssocMap.safeFromList . toListS $ assocMapS + assocMapS ~~ assocMap + assocMapS ~~ dataAssocMap + +unsafeFromListSpec :: Property +unsafeFromListSpec = property $ do + assocMapS <- forAll genAssocMapS + let assocMap = AssocMap.unsafeFromList . toListS $ assocMapS + dataAssocMap = Data.AssocMap.unsafeFromList . toListS $ assocMapS + assocMapS ~~ assocMap + assocMapS ~~ dataAssocMap + +lookupSpec :: Property +lookupSpec = property $ do + assocMapS <- forAll genAssocMapS + key <- forAll $ Gen.integral rangeElem + let assocMap = semanticsToAssocMap assocMapS + dataAssocMap = semanticsToDataAssocMap assocMapS + expected = lookupS key assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ lookupProgram + `unsafeApplyCode` (liftCodeDef key) + `unsafeApplyCode` (liftCodeDef assocMap) + ) + (===) + expected + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataLookupProgram + `unsafeApplyCode` (liftCodeDef key) + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + expected + +memberSpec :: Property +memberSpec = property $ do + assocMapS <- forAll genAssocMapS + key <- forAll $ Gen.integral rangeElem + let assocMap = semanticsToAssocMap assocMapS + dataAssocMap = semanticsToDataAssocMap assocMapS + expected = memberS key assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ memberProgram + `unsafeApplyCode` (liftCodeDef key) + `unsafeApplyCode` (liftCodeDef assocMap) + ) + (===) + expected + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataMemberProgram + `unsafeApplyCode` (liftCodeDef key) + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + expected + +insertSpec :: Property +insertSpec = property $ do + assocMapS <- forAll genAssocMapS + key <- forAll $ Gen.integral rangeElem + value <- forAll $ Gen.integral rangeElem + let assocMap = semanticsToAssocMap assocMapS + dataAssocMap = semanticsToDataAssocMap assocMapS + expected = sortS $ insertS key value assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ insertProgram + `unsafeApplyCode` (liftCodeDef key) + `unsafeApplyCode` (liftCodeDef value) + `unsafeApplyCode` (liftCodeDef assocMap) + ) + (===) + expected + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataInsertProgram + `unsafeApplyCode` (liftCodeDef key) + `unsafeApplyCode` (liftCodeDef value) + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + expected + +deleteSpec :: Property +deleteSpec = property $ do + assocMapS <- forAll genAssocMapS + key <- forAll $ Gen.integral rangeElem + let assocMap = semanticsToAssocMap assocMapS + dataAssocMap = semanticsToDataAssocMap assocMapS + expected = sortS $ deleteS key assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ deleteProgram + `unsafeApplyCode` (liftCodeDef key) + `unsafeApplyCode` (liftCodeDef assocMap) + ) + (===) + expected + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataDeleteProgram + `unsafeApplyCode` (liftCodeDef key) + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + expected + +allSpec :: Property +allSpec = property $ do + assocMapS <- forAll genAssocMapS + num <- forAll $ Gen.integral rangeElem + let assocMap = semanticsToAssocMap assocMapS + dataAssocMap = semanticsToDataAssocMap assocMapS + expected = allS (< num) assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ allProgram + `unsafeApplyCode` (liftCodeDef num) + `unsafeApplyCode` (liftCodeDef assocMap) + ) + (===) + expected + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataAllProgram + `unsafeApplyCode` (liftCodeDef num) + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + expected + +anySpec :: Property +anySpec = property $ do + assocMapS <- forAll genAssocMapS + num <- forAll $ Gen.integral rangeElem + let dataAssocMap = semanticsToDataAssocMap assocMapS + expected = anyS (< num) assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataAnyProgram + `unsafeApplyCode` (liftCodeDef num) + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + expected + +keysSpec :: Property +keysSpec = property $ do + assocMapS <- forAll genAssocMapS + let assocMap = semanticsToAssocMap assocMapS + expected = keysS assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ keysProgram + `unsafeApplyCode` (liftCodeDef assocMap) + ) + (===) + expected + +noDuplicateKeysSpec :: Property +noDuplicateKeysSpec = property $ do + assocMapS <- forAll genAssocMapS + let dataAssocMap = semanticsToDataAssocMap assocMapS + expected = noDuplicateKeysS assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataNoDuplicateKeysProgram + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + expected + +unionSpec :: Property +unionSpec = property $ do + -- resizing the generator for performance + assocMapS1 <- forAll (Gen.resize 20 genAssocMapS) + assocMapS2 <- forAll (Gen.resize 20 genAssocMapS) + let assocMap1 = semanticsToAssocMap assocMapS1 + assocMap2 = semanticsToAssocMap assocMapS2 + dataAssocMap1 = semanticsToDataAssocMap assocMapS1 + dataAssocMap2 = semanticsToDataAssocMap assocMapS2 + expected = mapS haskellToPlutusThese $ sortS $ unionS assocMapS1 assocMapS2 + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ unionProgram + `unsafeApplyCode` (liftCodeDef assocMap1) + `unsafeApplyCode` (liftCodeDef assocMap2) + ) + (===) + expected + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataUnionProgram + `unsafeApplyCode` (liftCodeDef dataAssocMap1) + `unsafeApplyCode` (liftCodeDef dataAssocMap2) + ) + (===) + expected + +unionWithSpec :: Property +unionWithSpec = property $ do + -- resizing the generator for performance + assocMapS1 <- forAll (Gen.resize 20 genAssocMapS) + assocMapS2 <- forAll (Gen.resize 20 genAssocMapS) + let assocMap1 = semanticsToAssocMap assocMapS1 + assocMap2 = semanticsToAssocMap assocMapS2 + dataAssocMap1 = semanticsToDataAssocMap assocMapS1 + dataAssocMap2 = semanticsToDataAssocMap assocMapS2 + merge i1 _ = i1 + expected = unionWithS merge assocMapS1 assocMapS2 + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ unionWithProgram + `unsafeApplyCode` (liftCodeDef assocMap1) + `unsafeApplyCode` (liftCodeDef assocMap2) + ) + (===) + expected + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ dataUnionWithProgram + `unsafeApplyCode` (liftCodeDef dataAssocMap1) + `unsafeApplyCode` (liftCodeDef dataAssocMap2) + ) + (===) + expected + +builtinDataEncodingSpec :: Property +builtinDataEncodingSpec = property $ do + assocMapS <- forAll genAssocMapS + let assocMap = semanticsToAssocMap assocMapS + dataAssocMap = semanticsToDataAssocMap assocMapS + unsafeRunTermCek + ( compiledCodeToTerm + $ encodedDataAssocMap `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + === + unsafeRunTermCek + ( compiledCodeToTerm + $ encodedAssocMap `unsafeApplyCode` (liftCodeDef assocMap) + ) + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ mDecodedAssocMap + `unsafeApplyCode` (liftCodeDef assocMap) + ) + (===) + (Just assocMapS) + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ mDecodedDataAssocMap + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + (Just assocMapS) + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ decodedAssocMap + `unsafeApplyCode` (liftCodeDef assocMap) + ) + (===) + assocMapS + cekResultMatchesHaskellValue + ( compiledCodeToTerm + $ decodedDataAssocMap + `unsafeApplyCode` (liftCodeDef dataAssocMap) + ) + (===) + assocMapS + +goldenTests :: TestNested +goldenTests = + testNestedGhc + "Budget" + [ goldenPirReadable "map1" map1 + , goldenUPlcReadable "map1" map1 + , goldenEvalCekCatch "map1" $ [map1 `unsafeApplyCode` (liftCodeDef 100)] + , goldenBudget "map1-budget" $ map1 `unsafeApplyCode` (liftCodeDef 100) + , goldenPirReadable "map2" map2 + , goldenUPlcReadable "map2" map2 + , goldenEvalCekCatch "map2" $ [map2 `unsafeApplyCode` (liftCodeDef 100)] + , goldenBudget "map2-budget" $ map2 `unsafeApplyCode` (liftCodeDef 100) + , goldenPirReadable "map3" map2 + , goldenUPlcReadable "map3" map2 + , goldenEvalCekCatch "map3" $ [map2 `unsafeApplyCode` (liftCodeDef 100)] + , goldenBudget "map3-budget" $ map2 `unsafeApplyCode` (liftCodeDef 100) + ] + +propertyTests :: TestTree +propertyTests = + testGroup "Map property tests" + [ testProperty "safeFromList" safeFromListSpec + , testProperty "unsafeFromList" unsafeFromListSpec + , testProperty "lookup" lookupSpec + , testProperty "member" memberSpec + , testProperty "insert" insertSpec + , testProperty "all" allSpec + , testProperty "any" anySpec + , testProperty "keys" keysSpec + , testProperty "noDuplicateKeys" noDuplicateKeysSpec + , testProperty "delete" deleteSpec + , testProperty "union" unionSpec + , testProperty "unionWith" unionWithSpec + , testProperty "builtinDataEncoding" builtinDataEncodingSpec + ] diff --git a/plutus-tx-plugin/test/Budget/9.6/map1-budget.budget.golden b/plutus-tx-plugin/test/Budget/9.6/map1-budget.budget.golden new file mode 100644 index 00000000000..3a0c427ec3e --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map1-budget.budget.golden @@ -0,0 +1,2 @@ +({cpu: 390169748 +| mem: 869909}) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map1.eval.golden b/plutus-tx-plugin/test/Budget/9.6/map1.eval.golden new file mode 100644 index 00000000000..2976eddf5c9 --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map1.eval.golden @@ -0,0 +1,8 @@ +(constr + 0 + (constr 0 (con bytestring #30)) + (constr 0 (con bytestring #35)) + (constr 0 (con bytestring #3130)) + (constr 1) + (constr 1) +) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map1.pir.golden b/plutus-tx-plugin/test/Budget/9.6/map1.pir.golden new file mode 100644 index 00000000000..dc42876c182 --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map1.pir.golden @@ -0,0 +1,404 @@ +letrec + data (List :: * -> *) a | List_match where + Nil : List a + Cons : a -> List a -> List a +in +letrec + !`$fEnumBool_$cenumFromTo` : integer -> integer -> List integer + = \(x : integer) (lim : integer) -> + ifThenElse + {all dead. List integer} + (lessThanEqualsInteger x lim) + (/\dead -> + Cons {integer} x (`$fEnumBool_$cenumFromTo` (addInteger 1 x) lim)) + (/\dead -> Nil {integer}) + {all dead. dead} +in +letrec + !go : List integer -> integer -> List integer + = \(acc : List integer) (n : integer) -> + let + !x : integer = quotientInteger n 10 + in + ifThenElse + {all dead. List integer} + (equalsInteger 0 x) + (/\dead -> Cons {integer} (remainderInteger n 10) acc) + (/\dead -> go (Cons {integer} (remainderInteger n 10) acc) x) + {all dead. dead} +in +letrec + !go : + List integer -> List string -> List string + = \(ds : List integer) -> + List_match + {integer} + ds + {all dead. List string -> List string} + (/\dead -> \(x : List string) -> x) + (\(x : integer) + (xs : List integer) -> + /\dead -> + let + !acc : List string -> List string = go xs + in + \(eta : List string) -> + Cons + {string} + (ifThenElse + {all dead. string} + (equalsInteger 0 x) + (/\dead -> "0") + (/\dead -> + ifThenElse + {all dead. string} + (equalsInteger 1 x) + (/\dead -> "1") + (/\dead -> + ifThenElse + {all dead. string} + (equalsInteger 2 x) + (/\dead -> "2") + (/\dead -> + ifThenElse + {all dead. string} + (equalsInteger 3 x) + (/\dead -> "3") + (/\dead -> + ifThenElse + {all dead. string} + (equalsInteger 4 x) + (/\dead -> "4") + (/\dead -> + ifThenElse + {all dead. string} + (equalsInteger 5 x) + (/\dead -> "5") + (/\dead -> + ifThenElse + {all dead. string} + (equalsInteger 6 x) + (/\dead -> "6") + (/\dead -> + ifThenElse + {all dead. string} + (equalsInteger 7 x) + (/\dead -> "7") + (/\dead -> + ifThenElse + {all dead. string} + (equalsInteger + 8 + x) + (/\dead -> "8") + (/\dead -> + ifThenElse + {string} + (equalsInteger + 9 + x) + "9" + "") + {all dead. dead}) + {all dead. dead}) + {all dead. dead}) + {all dead. dead}) + {all dead. dead}) + {all dead. dead}) + {all dead. dead}) + {all dead. dead}) + {all dead. dead}) + (acc eta)) + {all dead. dead} +in +letrec + !`$fShowBuiltinByteString_$cshowsPrec` : + integer -> integer -> List string -> List string + = \(p : integer) (n : integer) -> + ifThenElse + {all dead. List string -> List string} + (lessThanInteger n 0) + (/\dead -> + \(eta : List string) -> + Cons + {string} + "-" + (`$fShowBuiltinByteString_$cshowsPrec` + p + (subtractInteger 0 n) + eta)) + (/\dead -> go (go (Nil {integer}) n)) + {all dead. dead} +in +let + data (Tuple5 :: * -> * -> * -> * -> * -> *) a b c d e | Tuple5_match where + Tuple5 : a -> b -> c -> d -> e -> Tuple5 a b c d e + data (Tuple2 :: * -> * -> *) a b | Tuple2_match where + Tuple2 : a -> b -> Tuple2 a b +in +letrec + !go : all a. integer -> List a -> Tuple2 (List a) (List a) + = /\a -> + \(ds : integer) (ds : List a) -> + List_match + {a} + ds + {all dead. Tuple2 (List a) (List a)} + (/\dead -> Tuple2 {List a} {List a} (Nil {a}) (Nil {a})) + (\(y : a) (ys : List a) -> + /\dead -> + ifThenElse + {all dead. Tuple2 (List a) (List a)} + (equalsInteger 1 ds) + (/\dead -> + Tuple2 + {List a} + {List a} + ((let + a = List a + in + \(c : a -> a -> a) (n : a) -> c y n) + (\(ds : a) (ds : List a) -> Cons {a} ds ds) + (Nil {a})) + ys) + (/\dead -> + Tuple2_match + {List a} + {List a} + (go {a} (subtractInteger ds 1) ys) + {Tuple2 (List a) (List a)} + (\(zs : List a) (ws : List a) -> + Tuple2 {List a} {List a} (Cons {a} y zs) ws)) + {all dead. dead}) + {all dead. dead} +in +letrec + !go : List string -> integer + = \(ds : List string) -> + List_match + {string} + ds + {all dead. integer} + (/\dead -> 0) + (\(x : string) (xs : List string) -> /\dead -> addInteger 1 (go xs)) + {all dead. dead} +in +letrec + !concatBuiltinStrings : List string -> string + = \(ds : List string) -> + List_match + {string} + ds + {string} + "" + (\(x : string) (ds : List string) -> + List_match + {string} + ds + {all dead. string} + (/\dead -> x) + (\(ipv : string) (ipv : List string) -> + /\dead -> + Tuple2_match + {List string} + {List string} + (let + !n : integer = divideInteger (go ds) 2 + in + ifThenElse + {all dead. Tuple2 (List string) (List string)} + (lessThanEqualsInteger n 0) + (/\dead -> + Tuple2 {List string} {List string} (Nil {string}) ds) + (/\dead -> go {string} n ds) + {all dead. dead}) + {string} + (\(ipv : List string) (ipv : List string) -> + appendString + (concatBuiltinStrings ipv) + (concatBuiltinStrings ipv))) + {all dead. dead}) +in +let + data Unit | Unit_match where + Unit : Unit + data (Maybe :: * -> *) a | Maybe_match where + Just : a -> Maybe a + Nothing : Maybe a + !lookup : + all k a. + (\a -> a -> data) k -> + (\a -> data -> a) a -> + k -> + (\k a -> list (pair data data)) k a -> + Maybe a + = /\k a -> + \(`$dToData` : (\a -> a -> data) k) + (`$dUnsafeFromData` : (\a -> data -> a) a) + (ds : k) + (ds : (\k a -> list (pair data data)) k a) -> + Maybe_match + {data} + (let + !k : data = `$dToData` ds + in + letrec + !go : list (pair data data) -> Maybe data + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> Maybe data} + xs + (\(ds : Unit) -> Nothing {data}) + (\(ds : Unit) -> + let + !hd : pair data data = headList {pair data data} xs + in + ifThenElse + {all dead. Maybe data} + (equalsData k (fstPair {data} {data} hd)) + (/\dead -> + let + !ds : list (pair data data) + = tailList {pair data data} xs + in + Just {data} (sndPair {data} {data} hd)) + (/\dead -> go (tailList {pair data data} xs)) + {all dead. dead}) + Unit + in + go ds) + {all dead. Maybe a} + (\(a : data) -> /\dead -> Just {a} (`$dUnsafeFromData` a)) + (/\dead -> Nothing {a}) + {all dead. dead} + data Bool | Bool_match where + True : Bool + False : Bool +in +\(n : integer) -> + let + !nt : list (pair data data) + = (let + b = (\k a -> list (pair data data)) integer bytestring + in + \(k : integer -> b -> b) (z : b) -> + letrec + !go : List integer -> b + = \(ds : List integer) -> + List_match + {integer} + ds + {all dead. b} + (/\dead -> z) + (\(y : integer) (ys : List integer) -> + /\dead -> k y (go ys)) + {all dead. dead} + in + \(eta : List integer) -> go eta) + (\(i : integer) -> + let + !ds : integer = addInteger n i + !ds : bytestring + = encodeUtf8 + (concatBuiltinStrings + (`$fShowBuiltinByteString_$cshowsPrec` + 0 + i + (Nil {string}))) + in + \(ds : (\k a -> list (pair data data)) integer bytestring) -> + let + !k : data = iData ds + !a : data = bData ds + in + letrec + !go : list (pair data data) -> list (pair data data) + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> list (pair data data)} + xs + (\(ds : Unit) -> + mkCons {pair data data} (mkPairData k a) []) + (\(ds : Unit) -> + let + !hd : pair data data + = headList {pair data data} xs + !tl : list (pair data data) + = tailList {pair data data} xs + in + ifThenElse + {all dead. list (pair data data)} + (equalsData k (fstPair {data} {data} hd)) + (/\dead -> + mkCons {pair data data} (mkPairData k a) tl) + (/\dead -> mkCons {pair data data} hd (go tl)) + {all dead. dead}) + Unit + in + go ds) + (mkCons {pair data data} (mkPairData (iData n) (B #30)) []) + (`$fEnumBool_$cenumFromTo` 1 10) + !nt : list (pair data data) + = let + !k : data = iData (addInteger 5 n) + in + letrec + !go : list (pair data data) -> list (pair data data) + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> list (pair data data)} + xs + (\(ds : Unit) -> []) + (\(ds : Unit) -> + let + !hd : pair data data = headList {pair data data} xs + !tl : list (pair data data) = tailList {pair data data} xs + in + ifThenElse + {all dead. list (pair data data)} + (equalsData k (fstPair {data} {data} hd)) + (/\dead -> tl) + (/\dead -> mkCons {pair data data} hd (go tl)) + {all dead. dead}) + Unit + in + go nt + in + Tuple5 + {Maybe bytestring} + {Maybe bytestring} + {Maybe bytestring} + {Maybe bytestring} + {Maybe bytestring} + (lookup {integer} {bytestring} (\(i : integer) -> iData i) unBData n nt) + (lookup + {integer} + {bytestring} + (\(i : integer) -> iData i) + unBData + (addInteger 5 n) + nt) + (lookup + {integer} + {bytestring} + (\(i : integer) -> iData i) + unBData + (addInteger 10 n) + nt) + (lookup + {integer} + {bytestring} + (\(i : integer) -> iData i) + unBData + (addInteger 20 n) + nt) + (lookup + {integer} + {bytestring} + (\(i : integer) -> iData i) + unBData + (addInteger 5 n) + nt) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map1.uplc.golden b/plutus-tx-plugin/test/Budget/9.6/map1.uplc.golden new file mode 100644 index 00000000000..9553f47f06b --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map1.uplc.golden @@ -0,0 +1,412 @@ +program + 1.1.0 + ((\fix1 -> + (\`$fEnumBool_$cenumFromTo` -> + (\go -> + (\go -> + (\`$fShowBuiltinByteString_$cshowsPrec` -> + (\go -> + (\go -> + (\concatBuiltinStrings + n -> + (\nt -> + (\cse -> + (\nt -> + (\lookup -> + constr 0 + [ (lookup (\i -> iData i) unBData n nt) + , (lookup + (\i -> iData i) + unBData + cse + nt) + , (lookup + (\i -> iData i) + unBData + (addInteger 10 n) + nt) + , (lookup + (\i -> iData i) + unBData + (addInteger 20 n) + nt) + , (lookup + (\i -> iData i) + unBData + cse + nt) ]) + (\`$dToData` + `$dUnsafeFromData` + ds + ds -> + force + (case + ((\k -> + fix1 + (\go + xs -> + force + (force chooseList) + xs + (\ds -> constr 1 []) + (\ds -> + (\hd -> + force + (force + ifThenElse + (equalsData + k + (force + (force + fstPair) + hd)) + (delay + ((\ds -> + constr 0 + [ (force + (force + sndPair) + hd) ]) + (force + tailList + xs))) + (delay + (go + (force + tailList + xs))))) + (force headList + xs)) + (constr 0 [])) + ds) + (`$dToData` ds)) + [ (\a -> + delay + (constr 0 + [ (`$dUnsafeFromData` + a) ])) + , (delay (constr 1 [])) ]))) + ((\k -> + fix1 + (\go xs -> + force (force chooseList) + xs + (\ds -> []) + (\ds -> + (\hd -> + (\tl -> + force + (force ifThenElse + (equalsData + k + (force + (force + fstPair) + hd)) + (delay tl) + (delay + (force mkCons + hd + (go tl))))) + (force tailList xs)) + (force headList xs)) + (constr 0 [])) + nt) + (iData cse))) + (addInteger 5 n)) + ((\z -> + (\go eta -> + go eta) + (fix1 + (\go + ds -> + force + (case + ds + [ (delay z) + , (\y + ys -> + delay + ((\ds -> + (\ds + ds -> + (\k -> + (\a -> + fix1 + (\go + xs -> + force + (force + chooseList) + xs + (\ds -> + force + mkCons + (mkPairData + k + a) + [ ]) + (\ds -> + (\hd -> + (\tl -> + force + (force + ifThenElse + (equalsData + k + (force + (force + fstPair) + hd)) + (delay + (force + mkCons + (mkPairData + k + a) + tl)) + (delay + (force + mkCons + hd + (go + tl))))) + (force + tailList + xs)) + (force + headList + xs)) + (constr 0 + [])) + ds) + (bData ds)) + (iData ds)) + (encodeUtf8 + (concatBuiltinStrings + (`$fShowBuiltinByteString_$cshowsPrec` + 0 + y + (constr 0 + []))))) + (addInteger n y) + (go ys))) ])))) + (force mkCons (mkPairData (iData n) (B #30)) []) + (`$fEnumBool_$cenumFromTo` 1 10))) + (fix1 + (\concatBuiltinStrings + ds -> + case + ds + [ "" + , (\x + ds -> + force + (case + ds + [ (delay x) + , (\ipv + ipv -> + delay + (case + ((\n -> + force + (force + ifThenElse + (lessThanEqualsInteger + n + 0) + (delay + (constr 0 + [ (constr 0 + []) + , ds ])) + (delay + (force go + n + ds)))) + (divideInteger + (go ds) + 2)) + [ (\ipv + ipv -> + appendString + (concatBuiltinStrings + ipv) + (concatBuiltinStrings + ipv)) ])) ])) ]))) + (fix1 + (\go ds -> + force + (case + ds + [ (delay 0) + , (\x xs -> + delay (addInteger 1 (go xs))) ])))) + (fix1 + (\go + arg -> + delay + (\ds + ds -> + force + (case + ds + [ (delay + (constr 0 + [(constr 0 []), (constr 0 [])])) + , (\y + ys -> + delay + (force + (force + ifThenElse + (equalsInteger 1 ds) + (delay + (constr 0 + [ (constr 1 + [y, (constr 0 [])]) + , ys ])) + (delay + (case + (force + (go (delay (\x -> x))) + (subtractInteger ds 1) + ys) + [ (\zs + ws -> + constr 0 + [ (constr 1 + [y, zs]) + , ws ]) ]))))) ]))) + (delay (\x -> x)))) + (fix1 + (\`$fShowBuiltinByteString_$cshowsPrec` p n -> + force + (force ifThenElse + (lessThanInteger n 0) + (delay + (\eta -> + constr 1 + [ "-" + , (`$fShowBuiltinByteString_$cshowsPrec` + p + (subtractInteger 0 n) + eta) ])) + (delay (go (go (constr 0 []) n))))))) + (fix1 + (\go + ds -> + force + (case + ds + [ (delay (\x -> x)) + , (\x + xs -> + delay + ((\acc + eta -> + constr 1 + [ (force + (force + ifThenElse + (equalsInteger 0 x) + (delay "0") + (delay + (force + (force + ifThenElse + (equalsInteger 1 x) + (delay "1") + (delay + (force + (force + ifThenElse + (equalsInteger + 2 + x) + (delay "2") + (delay + (force + (force + ifThenElse + (equalsInteger + 3 + x) + (delay + "3") + (delay + (force + (force + ifThenElse + (equalsInteger + 4 + x) + (delay + "4") + (delay + (force + (force + ifThenElse + (equalsInteger + 5 + x) + (delay + "5") + (delay + (force + (force + ifThenElse + (equalsInteger + 6 + x) + (delay + "6") + (delay + (force + (force + ifThenElse + (equalsInteger + 7 + x) + (delay + "7") + (delay + (force + (force + ifThenElse + (equalsInteger + 8 + x) + (delay + "8") + (delay + (force + ifThenElse + (equalsInteger + 9 + x) + "9" + "")))))))))))))))))))))))))))) + , (acc eta) ]) + (go xs))) ])))) + (fix1 + (\go acc n -> + (\x -> + force + (force ifThenElse + (equalsInteger 0 x) + (delay (constr 1 [(remainderInteger n 10), acc])) + (delay + (go (constr 1 [(remainderInteger n 10), acc]) x)))) + (quotientInteger n 10)))) + (fix1 + (\`$fEnumBool_$cenumFromTo` x lim -> + force + (force ifThenElse + (lessThanEqualsInteger x lim) + (delay + (constr 1 + [x, (`$fEnumBool_$cenumFromTo` (addInteger 1 x) lim)])) + (delay (constr 0 [])))))) + (\f -> (\s -> s s) (\s -> f (\x -> s s x)))) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map2-budget.budget.golden b/plutus-tx-plugin/test/Budget/9.6/map2-budget.budget.golden new file mode 100644 index 00000000000..2c63bc124a1 --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map2-budget.budget.golden @@ -0,0 +1,2 @@ +({cpu: 155458417 +| mem: 394122}) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map2.eval.golden b/plutus-tx-plugin/test/Budget/9.6/map2.eval.golden new file mode 100644 index 00000000000..e8e3b12565c --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map2.eval.golden @@ -0,0 +1,27 @@ +(constr + 1 + (constr 0 (con integer 105) (con string "five")) + (constr + 1 + (constr 0 (con integer 104) (con string "fourFOUR")) + (constr + 1 + (constr 0 (con integer 103) (con string "threeTHREE")) + (constr + 1 + (constr 0 (con integer 102) (con string "two")) + (constr + 1 + (constr 0 (con integer 101) (con string "one")) + (constr + 1 + (constr 0 (con integer 106) (con string "SIX")) + (constr + 1 (constr 0 (con integer 107) (con string "SEVEN")) (constr 0) + ) + ) + ) + ) + ) + ) +) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map2.pir.golden b/plutus-tx-plugin/test/Budget/9.6/map2.pir.golden new file mode 100644 index 00000000000..c735c68c517 --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map2.pir.golden @@ -0,0 +1,338 @@ +let + data Unit | Unit_match where + Unit : Unit + data (Tuple2 :: * -> * -> *) a b | Tuple2_match where + Tuple2 : a -> b -> Tuple2 a b +in +letrec + data (List :: * -> *) a | List_match where + Nil : List a + Cons : a -> List a -> List a +in +letrec + !go : list (pair data data) -> List (Tuple2 integer bytestring) + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> List (Tuple2 integer bytestring)} + xs + (\(ds : Unit) -> Nil {Tuple2 integer bytestring}) + (\(ds : Unit) -> + let + !hd : pair data data = headList {pair data data} xs + !tl : list (pair data data) = tailList {pair data data} xs + in + Cons + {Tuple2 integer bytestring} + (Tuple2 + {integer} + {bytestring} + (unIData (fstPair {data} {data} hd)) + (unBData (sndPair {data} {data} hd))) + (go tl)) + Unit +in +letrec + !go : list (pair data data) -> list (pair data data) -> list (pair data data) + = \(acc : list (pair data data)) (xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> list (pair data data)} + xs + (\(ds : Unit) -> acc) + (\(ds : Unit) -> + go + (mkCons {pair data data} (headList {pair data data} xs) acc) + (tailList {pair data data} xs)) + Unit +in +let + data (Maybe :: * -> *) a | Maybe_match where + Just : a -> Maybe a + Nothing : Maybe a + data Bool | Bool_match where + True : Bool + False : Bool +in +letrec + !goList : List (Tuple2 data data) -> list (pair data data) + = \(ds : List (Tuple2 data data)) -> + List_match + {Tuple2 data data} + ds + {all dead. list (pair data data)} + (/\dead -> []) + (\(d : Tuple2 data data) (ds : List (Tuple2 data data)) -> + /\dead -> + mkCons + {pair data data} + (Tuple2_match + {data} + {data} + d + {pair data data} + (\(d : data) (d : data) -> mkPairData d d)) + (goList ds)) + {all dead. dead} +in +let + !unsafeFromList : + all k a. + (\a -> a -> data) k -> + (\a -> a -> data) a -> + List (Tuple2 k a) -> + (\k a -> list (pair data data)) k a + = /\k a -> + \(`$dToData` : (\a -> a -> data) k) + (`$dToData` : (\a -> a -> data) a) -> + letrec + !go : List (Tuple2 k a) -> List (Tuple2 data data) + = \(ds : List (Tuple2 k a)) -> + List_match + {Tuple2 k a} + ds + {all dead. List (Tuple2 data data)} + (/\dead -> Nil {Tuple2 data data}) + (\(x : Tuple2 k a) (xs : List (Tuple2 k a)) -> + /\dead -> + Cons + {Tuple2 data data} + (Tuple2_match + {k} + {a} + x + {Tuple2 data data} + (\(k : k) (a : a) -> + Tuple2 + {data} + {data} + (`$dToData` k) + (`$dToData` a))) + (go xs)) + {all dead. dead} + in + \(eta : List (Tuple2 k a)) -> + let + !eta : List (Tuple2 data data) = go eta + in + goList eta +in +\(n : integer) -> + let + !nt : list (pair data data) + = unsafeFromList + {integer} + {bytestring} + (\(i : integer) -> iData i) + bData + ((let + a = Tuple2 integer bytestring + in + \(g : all b. (a -> b -> b) -> b -> b) -> + g {List a} (\(ds : a) (ds : List a) -> Cons {a} ds ds) (Nil {a})) + (/\a -> + \(c : Tuple2 integer bytestring -> a -> a) (n : a) -> + c + (Tuple2 {integer} {bytestring} (addInteger 3 n) #5448524545) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 4 n) + #464f5552) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 6 n) + #534958) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 7 n) + #534556454e) + n))))) + in + letrec + !go : list (pair data data) -> list (pair data data) + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> list (pair data data)} + xs + (\(ds : Unit) -> []) + (\(ds : Unit) -> + let + !hd : pair data data = headList {pair data data} xs + !tl : list (pair data data) = tailList {pair data data} xs + !v' : data = sndPair {data} {data} hd + !k' : data = fstPair {data} {data} hd + in + letrec + !go : list (pair data data) -> Maybe data + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> Maybe data} + xs + (\(ds : Unit) -> Nothing {data}) + (\(ds : Unit) -> + let + !hd : pair data data + = headList {pair data data} xs + in + ifThenElse + {all dead. Maybe data} + (equalsData k' (fstPair {data} {data} hd)) + (/\dead -> + let + !ds : list (pair data data) + = tailList {pair data data} xs + in + Just {data} (sndPair {data} {data} hd)) + (/\dead -> go (tailList {pair data data} xs)) + {all dead. dead}) + Unit + in + Maybe_match + {data} + (go nt) + {all dead. list (pair data data)} + (\(r : data) -> + /\dead -> + mkCons + {pair data data} + (mkPairData + k' + (bData (appendByteString (unBData v') (unBData r)))) + (go tl)) + (/\dead -> mkCons {pair data data} (mkPairData k' v') (go tl)) + {all dead. dead}) + Unit + in + let + !nt : list (pair data data) + = unsafeFromList + {integer} + {bytestring} + (\(i : integer) -> iData i) + bData + ((let + a = Tuple2 integer bytestring + in + \(g : all b. (a -> b -> b) -> b -> b) -> + g {List a} (\(ds : a) (ds : List a) -> Cons {a} ds ds) (Nil {a})) + (/\a -> + \(c : Tuple2 integer bytestring -> a -> a) (n : a) -> + c + (Tuple2 {integer} {bytestring} (addInteger 1 n) #6f6e65) + (c + (Tuple2 {integer} {bytestring} (addInteger 2 n) #74776f) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 3 n) + #7468726565) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 4 n) + #666f7572) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 5 n) + #66697665) + n)))))) + in + letrec + !go : list (pair data data) -> list (pair data data) + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> list (pair data data)} + xs + (\(ds : Unit) -> []) + (\(ds : Unit) -> + let + !hd : pair data data = headList {pair data data} xs + !tl' : list (pair data data) + = go (tailList {pair data data} xs) + in + Bool_match + (let + !k : data = fstPair {data} {data} hd + in + letrec + !go : list (pair data data) -> Bool + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> Bool} + xs + (\(ds : Unit) -> False) + (\(ds : Unit) -> + ifThenElse + {all dead. Bool} + (equalsData + k + (fstPair + {data} + {data} + (headList {pair data data} xs))) + (/\dead -> + let + !ds : list (pair data data) + = tailList {pair data data} xs + in + True) + (/\dead -> go (tailList {pair data data} xs)) + {all dead. dead}) + Unit + in + go nt) + {all dead. list (pair data data)} + (/\dead -> tl') + (/\dead -> mkCons {pair data data} hd tl') + {all dead. dead}) + Unit + in + let + !nt : list (pair data data) + = let + !rs' : list (pair data data) = go nt + !ls' : list (pair data data) = go nt + in + go rs' ls' + in + (let + a = Tuple2 integer bytestring + in + /\b -> + \(f : a -> b) -> + letrec + !go : List a -> List b + = \(ds : List a) -> + List_match + {a} + ds + {all dead. List b} + (/\dead -> Nil {b}) + (\(x : a) (xs : List a) -> /\dead -> Cons {b} (f x) (go xs)) + {all dead. dead} + in + \(eta : List a) -> go eta) + {Tuple2 integer string} + (\(ds : Tuple2 integer bytestring) -> + Tuple2_match + {integer} + {bytestring} + ds + {Tuple2 integer string} + (\(k : integer) (v : bytestring) -> + Tuple2 {integer} {string} k (decodeUtf8 v))) + (go nt) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map2.uplc.golden b/plutus-tx-plugin/test/Budget/9.6/map2.uplc.golden new file mode 100644 index 00000000000..f1bf99b0f21 --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map2.uplc.golden @@ -0,0 +1,272 @@ +program + 1.1.0 + ((\fix1 -> + (\go -> + (\go -> + (\goList + n -> + (\unsafeFromList -> + (\cse -> + (\cse -> + (\nt -> + (\go -> + (\nt -> + (\nt -> + fix1 + (\go + ds -> + force + (case + ds + [ (delay (constr 0 [])) + , (\x + xs -> + delay + (constr 1 + [ (case + x + [ (\k + v -> + constr 0 + [ k + , (decodeUtf8 + v) ]) ]) + , (go xs) ])) ])) + (go nt)) + ((\rs' -> + (\ls' -> go rs' ls') (go nt)) + (fix1 + (\go + xs -> + force + (force chooseList) + xs + (\ds -> []) + (\ds -> + (\hd -> + (\tl' -> + force + (case + ((\k -> + fix1 + (\go + xs -> + force + (force + chooseList) + xs + (\ds -> + constr 1 + []) + (\ds -> + force + (force + ifThenElse + (equalsData + k + (force + (force + fstPair) + (force + headList + xs))) + (delay + ((\ds -> + constr 0 + [ ]) + (force + tailList + xs))) + (delay + (go + (force + tailList + xs))))) + (constr 0 + [])) + nt) + (force + (force + fstPair) + hd)) + [ (delay tl') + , (delay + (force mkCons + hd + tl')) ])) + (go (force tailList xs))) + (force headList xs)) + (constr 0 [])) + nt))) + (unsafeFromList + (\i -> iData i) + bData + (constr 1 + [ (constr 0 [(addInteger 1 n), #6f6e65]) + , (constr 1 + [ (constr 0 + [(addInteger 2 n), #74776f]) + , (constr 1 + [ (constr 0 [cse, #7468726565]) + , (constr 1 + [ (constr 0 + [cse, #666f7572]) + , (constr 1 + [ (constr 0 + [ (addInteger + 5 + n) + , #66697665 ]) + , (constr 0 + [ ]) ]) ]) ]) ]) ]))) + (fix1 + (\go + xs -> + force + (force chooseList) + xs + (\ds -> []) + (\ds -> + (\hd -> + (\tl -> + (\v' -> + (\k' -> + force + (case + (fix1 + (\go + xs -> + force + (force + chooseList) + xs + (\ds -> + constr 1 []) + (\ds -> + (\hd -> + force + (force + ifThenElse + (equalsData + k' + (force + (force + fstPair) + hd)) + (delay + ((\ds -> + constr 0 + [ (force + (force + sndPair) + hd) ]) + (force + tailList + xs))) + (delay + (go + (force + tailList + xs))))) + (force + headList + xs)) + (constr 0 [])) + nt) + [ (\r -> + delay + (force + mkCons + (mkPairData + k' + (bData + (appendByteString + (unBData + v') + (unBData + r)))) + (go tl))) + , (delay + (force mkCons + (mkPairData + k' + v') + (go tl))) ])) + (force (force fstPair) hd)) + (force (force sndPair) hd)) + (force tailList xs)) + (force headList xs)) + (constr 0 [])))) + (unsafeFromList + (\i -> iData i) + bData + (constr 1 + [ (constr 0 [cse, #5448524545]) + , (constr 1 + [ (constr 0 [cse, #464f5552]) + , (constr 1 + [ (constr 0 + [(addInteger 6 n), #534958]) + , (constr 1 + [ (constr 0 + [ (addInteger 7 n) + , #534556454e ]) + , (constr 0 []) ]) ]) ]) ]))) + (addInteger 4 n)) + (addInteger 3 n)) + (\`$dToData` `$dToData` -> + (\go eta -> goList (go eta)) + (fix1 + (\go ds -> + force + (case + ds + [ (delay (constr 0 [])) + , (\x xs -> + delay + (constr 1 + [ (case + x + [ (\k a -> + constr 0 + [ (`$dToData` k) + , (`$dToData` a) ]) ]) + , (go xs) ])) ]))))) + (fix1 + (\goList ds -> + force + (case + ds + [ (delay []) + , (\d ds -> + delay + (force mkCons + (case d [(\d d -> mkPairData d d)]) + (goList ds))) ])))) + (fix1 + (\go acc xs -> + force (force chooseList) + xs + (\ds -> acc) + (\ds -> + go + (force mkCons (force headList xs) acc) + (force tailList xs)) + (constr 0 [])))) + (fix1 + (\go xs -> + force (force chooseList) + xs + (\ds -> constr 0 []) + (\ds -> + (\hd -> + (\tl -> + constr 1 + [ (constr 0 + [ (unIData (force (force fstPair) hd)) + , (unBData (force (force sndPair) hd)) ]) + , (go tl) ]) + (force tailList xs)) + (force headList xs)) + (constr 0 [])))) + (\f -> (\s -> s s) (\s -> f (\x -> s s x)))) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map3-budget.budget.golden b/plutus-tx-plugin/test/Budget/9.6/map3-budget.budget.golden new file mode 100644 index 00000000000..2c63bc124a1 --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map3-budget.budget.golden @@ -0,0 +1,2 @@ +({cpu: 155458417 +| mem: 394122}) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map3.eval.golden b/plutus-tx-plugin/test/Budget/9.6/map3.eval.golden new file mode 100644 index 00000000000..e8e3b12565c --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map3.eval.golden @@ -0,0 +1,27 @@ +(constr + 1 + (constr 0 (con integer 105) (con string "five")) + (constr + 1 + (constr 0 (con integer 104) (con string "fourFOUR")) + (constr + 1 + (constr 0 (con integer 103) (con string "threeTHREE")) + (constr + 1 + (constr 0 (con integer 102) (con string "two")) + (constr + 1 + (constr 0 (con integer 101) (con string "one")) + (constr + 1 + (constr 0 (con integer 106) (con string "SIX")) + (constr + 1 (constr 0 (con integer 107) (con string "SEVEN")) (constr 0) + ) + ) + ) + ) + ) + ) +) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map3.pir.golden b/plutus-tx-plugin/test/Budget/9.6/map3.pir.golden new file mode 100644 index 00000000000..c735c68c517 --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map3.pir.golden @@ -0,0 +1,338 @@ +let + data Unit | Unit_match where + Unit : Unit + data (Tuple2 :: * -> * -> *) a b | Tuple2_match where + Tuple2 : a -> b -> Tuple2 a b +in +letrec + data (List :: * -> *) a | List_match where + Nil : List a + Cons : a -> List a -> List a +in +letrec + !go : list (pair data data) -> List (Tuple2 integer bytestring) + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> List (Tuple2 integer bytestring)} + xs + (\(ds : Unit) -> Nil {Tuple2 integer bytestring}) + (\(ds : Unit) -> + let + !hd : pair data data = headList {pair data data} xs + !tl : list (pair data data) = tailList {pair data data} xs + in + Cons + {Tuple2 integer bytestring} + (Tuple2 + {integer} + {bytestring} + (unIData (fstPair {data} {data} hd)) + (unBData (sndPair {data} {data} hd))) + (go tl)) + Unit +in +letrec + !go : list (pair data data) -> list (pair data data) -> list (pair data data) + = \(acc : list (pair data data)) (xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> list (pair data data)} + xs + (\(ds : Unit) -> acc) + (\(ds : Unit) -> + go + (mkCons {pair data data} (headList {pair data data} xs) acc) + (tailList {pair data data} xs)) + Unit +in +let + data (Maybe :: * -> *) a | Maybe_match where + Just : a -> Maybe a + Nothing : Maybe a + data Bool | Bool_match where + True : Bool + False : Bool +in +letrec + !goList : List (Tuple2 data data) -> list (pair data data) + = \(ds : List (Tuple2 data data)) -> + List_match + {Tuple2 data data} + ds + {all dead. list (pair data data)} + (/\dead -> []) + (\(d : Tuple2 data data) (ds : List (Tuple2 data data)) -> + /\dead -> + mkCons + {pair data data} + (Tuple2_match + {data} + {data} + d + {pair data data} + (\(d : data) (d : data) -> mkPairData d d)) + (goList ds)) + {all dead. dead} +in +let + !unsafeFromList : + all k a. + (\a -> a -> data) k -> + (\a -> a -> data) a -> + List (Tuple2 k a) -> + (\k a -> list (pair data data)) k a + = /\k a -> + \(`$dToData` : (\a -> a -> data) k) + (`$dToData` : (\a -> a -> data) a) -> + letrec + !go : List (Tuple2 k a) -> List (Tuple2 data data) + = \(ds : List (Tuple2 k a)) -> + List_match + {Tuple2 k a} + ds + {all dead. List (Tuple2 data data)} + (/\dead -> Nil {Tuple2 data data}) + (\(x : Tuple2 k a) (xs : List (Tuple2 k a)) -> + /\dead -> + Cons + {Tuple2 data data} + (Tuple2_match + {k} + {a} + x + {Tuple2 data data} + (\(k : k) (a : a) -> + Tuple2 + {data} + {data} + (`$dToData` k) + (`$dToData` a))) + (go xs)) + {all dead. dead} + in + \(eta : List (Tuple2 k a)) -> + let + !eta : List (Tuple2 data data) = go eta + in + goList eta +in +\(n : integer) -> + let + !nt : list (pair data data) + = unsafeFromList + {integer} + {bytestring} + (\(i : integer) -> iData i) + bData + ((let + a = Tuple2 integer bytestring + in + \(g : all b. (a -> b -> b) -> b -> b) -> + g {List a} (\(ds : a) (ds : List a) -> Cons {a} ds ds) (Nil {a})) + (/\a -> + \(c : Tuple2 integer bytestring -> a -> a) (n : a) -> + c + (Tuple2 {integer} {bytestring} (addInteger 3 n) #5448524545) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 4 n) + #464f5552) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 6 n) + #534958) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 7 n) + #534556454e) + n))))) + in + letrec + !go : list (pair data data) -> list (pair data data) + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> list (pair data data)} + xs + (\(ds : Unit) -> []) + (\(ds : Unit) -> + let + !hd : pair data data = headList {pair data data} xs + !tl : list (pair data data) = tailList {pair data data} xs + !v' : data = sndPair {data} {data} hd + !k' : data = fstPair {data} {data} hd + in + letrec + !go : list (pair data data) -> Maybe data + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> Maybe data} + xs + (\(ds : Unit) -> Nothing {data}) + (\(ds : Unit) -> + let + !hd : pair data data + = headList {pair data data} xs + in + ifThenElse + {all dead. Maybe data} + (equalsData k' (fstPair {data} {data} hd)) + (/\dead -> + let + !ds : list (pair data data) + = tailList {pair data data} xs + in + Just {data} (sndPair {data} {data} hd)) + (/\dead -> go (tailList {pair data data} xs)) + {all dead. dead}) + Unit + in + Maybe_match + {data} + (go nt) + {all dead. list (pair data data)} + (\(r : data) -> + /\dead -> + mkCons + {pair data data} + (mkPairData + k' + (bData (appendByteString (unBData v') (unBData r)))) + (go tl)) + (/\dead -> mkCons {pair data data} (mkPairData k' v') (go tl)) + {all dead. dead}) + Unit + in + let + !nt : list (pair data data) + = unsafeFromList + {integer} + {bytestring} + (\(i : integer) -> iData i) + bData + ((let + a = Tuple2 integer bytestring + in + \(g : all b. (a -> b -> b) -> b -> b) -> + g {List a} (\(ds : a) (ds : List a) -> Cons {a} ds ds) (Nil {a})) + (/\a -> + \(c : Tuple2 integer bytestring -> a -> a) (n : a) -> + c + (Tuple2 {integer} {bytestring} (addInteger 1 n) #6f6e65) + (c + (Tuple2 {integer} {bytestring} (addInteger 2 n) #74776f) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 3 n) + #7468726565) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 4 n) + #666f7572) + (c + (Tuple2 + {integer} + {bytestring} + (addInteger 5 n) + #66697665) + n)))))) + in + letrec + !go : list (pair data data) -> list (pair data data) + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> list (pair data data)} + xs + (\(ds : Unit) -> []) + (\(ds : Unit) -> + let + !hd : pair data data = headList {pair data data} xs + !tl' : list (pair data data) + = go (tailList {pair data data} xs) + in + Bool_match + (let + !k : data = fstPair {data} {data} hd + in + letrec + !go : list (pair data data) -> Bool + = \(xs : list (pair data data)) -> + chooseList + {pair data data} + {Unit -> Bool} + xs + (\(ds : Unit) -> False) + (\(ds : Unit) -> + ifThenElse + {all dead. Bool} + (equalsData + k + (fstPair + {data} + {data} + (headList {pair data data} xs))) + (/\dead -> + let + !ds : list (pair data data) + = tailList {pair data data} xs + in + True) + (/\dead -> go (tailList {pair data data} xs)) + {all dead. dead}) + Unit + in + go nt) + {all dead. list (pair data data)} + (/\dead -> tl') + (/\dead -> mkCons {pair data data} hd tl') + {all dead. dead}) + Unit + in + let + !nt : list (pair data data) + = let + !rs' : list (pair data data) = go nt + !ls' : list (pair data data) = go nt + in + go rs' ls' + in + (let + a = Tuple2 integer bytestring + in + /\b -> + \(f : a -> b) -> + letrec + !go : List a -> List b + = \(ds : List a) -> + List_match + {a} + ds + {all dead. List b} + (/\dead -> Nil {b}) + (\(x : a) (xs : List a) -> /\dead -> Cons {b} (f x) (go xs)) + {all dead. dead} + in + \(eta : List a) -> go eta) + {Tuple2 integer string} + (\(ds : Tuple2 integer bytestring) -> + Tuple2_match + {integer} + {bytestring} + ds + {Tuple2 integer string} + (\(k : integer) (v : bytestring) -> + Tuple2 {integer} {string} k (decodeUtf8 v))) + (go nt) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/9.6/map3.uplc.golden b/plutus-tx-plugin/test/Budget/9.6/map3.uplc.golden new file mode 100644 index 00000000000..f1bf99b0f21 --- /dev/null +++ b/plutus-tx-plugin/test/Budget/9.6/map3.uplc.golden @@ -0,0 +1,272 @@ +program + 1.1.0 + ((\fix1 -> + (\go -> + (\go -> + (\goList + n -> + (\unsafeFromList -> + (\cse -> + (\cse -> + (\nt -> + (\go -> + (\nt -> + (\nt -> + fix1 + (\go + ds -> + force + (case + ds + [ (delay (constr 0 [])) + , (\x + xs -> + delay + (constr 1 + [ (case + x + [ (\k + v -> + constr 0 + [ k + , (decodeUtf8 + v) ]) ]) + , (go xs) ])) ])) + (go nt)) + ((\rs' -> + (\ls' -> go rs' ls') (go nt)) + (fix1 + (\go + xs -> + force + (force chooseList) + xs + (\ds -> []) + (\ds -> + (\hd -> + (\tl' -> + force + (case + ((\k -> + fix1 + (\go + xs -> + force + (force + chooseList) + xs + (\ds -> + constr 1 + []) + (\ds -> + force + (force + ifThenElse + (equalsData + k + (force + (force + fstPair) + (force + headList + xs))) + (delay + ((\ds -> + constr 0 + [ ]) + (force + tailList + xs))) + (delay + (go + (force + tailList + xs))))) + (constr 0 + [])) + nt) + (force + (force + fstPair) + hd)) + [ (delay tl') + , (delay + (force mkCons + hd + tl')) ])) + (go (force tailList xs))) + (force headList xs)) + (constr 0 [])) + nt))) + (unsafeFromList + (\i -> iData i) + bData + (constr 1 + [ (constr 0 [(addInteger 1 n), #6f6e65]) + , (constr 1 + [ (constr 0 + [(addInteger 2 n), #74776f]) + , (constr 1 + [ (constr 0 [cse, #7468726565]) + , (constr 1 + [ (constr 0 + [cse, #666f7572]) + , (constr 1 + [ (constr 0 + [ (addInteger + 5 + n) + , #66697665 ]) + , (constr 0 + [ ]) ]) ]) ]) ]) ]))) + (fix1 + (\go + xs -> + force + (force chooseList) + xs + (\ds -> []) + (\ds -> + (\hd -> + (\tl -> + (\v' -> + (\k' -> + force + (case + (fix1 + (\go + xs -> + force + (force + chooseList) + xs + (\ds -> + constr 1 []) + (\ds -> + (\hd -> + force + (force + ifThenElse + (equalsData + k' + (force + (force + fstPair) + hd)) + (delay + ((\ds -> + constr 0 + [ (force + (force + sndPair) + hd) ]) + (force + tailList + xs))) + (delay + (go + (force + tailList + xs))))) + (force + headList + xs)) + (constr 0 [])) + nt) + [ (\r -> + delay + (force + mkCons + (mkPairData + k' + (bData + (appendByteString + (unBData + v') + (unBData + r)))) + (go tl))) + , (delay + (force mkCons + (mkPairData + k' + v') + (go tl))) ])) + (force (force fstPair) hd)) + (force (force sndPair) hd)) + (force tailList xs)) + (force headList xs)) + (constr 0 [])))) + (unsafeFromList + (\i -> iData i) + bData + (constr 1 + [ (constr 0 [cse, #5448524545]) + , (constr 1 + [ (constr 0 [cse, #464f5552]) + , (constr 1 + [ (constr 0 + [(addInteger 6 n), #534958]) + , (constr 1 + [ (constr 0 + [ (addInteger 7 n) + , #534556454e ]) + , (constr 0 []) ]) ]) ]) ]))) + (addInteger 4 n)) + (addInteger 3 n)) + (\`$dToData` `$dToData` -> + (\go eta -> goList (go eta)) + (fix1 + (\go ds -> + force + (case + ds + [ (delay (constr 0 [])) + , (\x xs -> + delay + (constr 1 + [ (case + x + [ (\k a -> + constr 0 + [ (`$dToData` k) + , (`$dToData` a) ]) ]) + , (go xs) ])) ]))))) + (fix1 + (\goList ds -> + force + (case + ds + [ (delay []) + , (\d ds -> + delay + (force mkCons + (case d [(\d d -> mkPairData d d)]) + (goList ds))) ])))) + (fix1 + (\go acc xs -> + force (force chooseList) + xs + (\ds -> acc) + (\ds -> + go + (force mkCons (force headList xs) acc) + (force tailList xs)) + (constr 0 [])))) + (fix1 + (\go xs -> + force (force chooseList) + xs + (\ds -> constr 0 []) + (\ds -> + (\hd -> + (\tl -> + constr 1 + [ (constr 0 + [ (unIData (force (force fstPair) hd)) + , (unBData (force (force sndPair) hd)) ]) + , (go tl) ]) + (force tailList xs)) + (force headList xs)) + (constr 0 [])))) + (\f -> (\s -> s s) (\s -> f (\x -> s s x)))) \ No newline at end of file diff --git a/plutus-tx-plugin/test/Budget/Spec.hs b/plutus-tx-plugin/test/Budget/Spec.hs index b9200ed45c8..891c9d27b91 100644 --- a/plutus-tx-plugin/test/Budget/Spec.hs +++ b/plutus-tx-plugin/test/Budget/Spec.hs @@ -19,7 +19,7 @@ import Test.Tasty.Extras import Budget.WithGHCOptimisations qualified as WithGHCOptTest import Budget.WithoutGHCOptimisations qualified as WithoutGHCOptTest import PlutusTx.AsData qualified as AsData -import PlutusTx.Builtins qualified as PlutusTx +import PlutusTx.Builtins qualified as PlutusTx hiding (null) import PlutusTx.Builtins.Internal qualified as BI import PlutusTx.Code import PlutusTx.IsData qualified as IsData diff --git a/plutus-tx-plugin/test/Spec.hs b/plutus-tx-plugin/test/Spec.hs index 5847ced49e5..85228a20758 100644 --- a/plutus-tx-plugin/test/Spec.hs +++ b/plutus-tx-plugin/test/Spec.hs @@ -1,6 +1,7 @@ module Main (main) where import AsData.Budget.Spec qualified as AsData.Budget +import AssocMap.Spec qualified as AssocMap import Blueprint.Tests qualified import Budget.Spec qualified as Budget import IntegerLiterals.NoStrict.NegativeLiterals.Spec qualified @@ -20,7 +21,7 @@ import TH.Spec qualified as TH import Unicode.Spec qualified as Unicode main :: IO () -main = defaultMain $ runTestNestedIn ["test"] tests +main = defaultMain $ testGroup "" [runTestNestedIn ["test"] tests, AssocMap.propertyTests] tests :: TestNested tests = @@ -42,4 +43,5 @@ tests = , Strictness.tests , Blueprint.Tests.goldenTests , pure Unicode.tests + , AssocMap.goldenTests ] diff --git a/plutus-tx-plugin/test/Util/Common.hs b/plutus-tx-plugin/test/Util/Common.hs new file mode 100644 index 00000000000..5f5a866e38d --- /dev/null +++ b/plutus-tx-plugin/test/Util/Common.hs @@ -0,0 +1,88 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ViewPatterns #-} + +module Util.Common + ( Program + , Term + , toAnonDeBruijnTerm + , toNamedDeBruijnTerm + , compiledCodeToTerm + , haskellValueToTerm + , unsafeRunTermCek + , runTermCek + , cekResultMatchesHaskellValue + ) +where + +import PlutusTx qualified as Tx + +import PlutusCore qualified as PLC +import PlutusCore.Default +import PlutusCore.Evaluation.Machine.ExBudgetingDefaults qualified as PLC + +import UntypedPlutusCore qualified as UPLC +import UntypedPlutusCore.Evaluation.Machine.Cek as Cek + +import Data.Text (Text) + +type Term = UPLC.Term PLC.NamedDeBruijn DefaultUni DefaultFun () +type Program = UPLC.Program PLC.NamedDeBruijn DefaultUni DefaultFun () + +{- | Given a DeBruijn-named term, give every variable the name "v". If we later + call unDeBruijn, that will rename the variables to things like "v123", where + 123 is the relevant de Bruijn index.-} +toNamedDeBruijnTerm + :: UPLC.Term UPLC.DeBruijn DefaultUni DefaultFun () + -> UPLC.Term UPLC.NamedDeBruijn DefaultUni DefaultFun () +toNamedDeBruijnTerm = UPLC.termMapNames UPLC.fakeNameDeBruijn + +{- | Remove the textual names from a NamedDeBruijn term -} +toAnonDeBruijnTerm + :: Term + -> UPLC.Term UPLC.DeBruijn DefaultUni DefaultFun () +toAnonDeBruijnTerm = UPLC.termMapNames UPLC.unNameDeBruijn + +{- | Just extract the body of a program wrapped in a 'CompiledCodeIn'. We use this a lot. -} +compiledCodeToTerm + :: Tx.CompiledCodeIn DefaultUni DefaultFun a -> Term +compiledCodeToTerm (Tx.getPlcNoAnn -> UPLC.Program _ _ body) = body + +{- | Lift a Haskell value to a PLC term. The constraints get a bit out of control + if we try to do this over an arbitrary universe.-} +haskellValueToTerm + :: Tx.Lift DefaultUni a => a -> Term +haskellValueToTerm = compiledCodeToTerm . Tx.liftCodeDef + +{- | Just run a term to obtain an `EvaluationResult` (used for tests etc.) -} +unsafeRunTermCek :: Term -> EvaluationResult Term +unsafeRunTermCek = + unsafeExtractEvaluationResult + . (\(res, _, _) -> res) + . runCekDeBruijn PLC.defaultCekParameters Cek.restrictingEnormous Cek.noEmitter + +-- | Just run a term. +runTermCek :: + Term -> + ( Either (CekEvaluationException UPLC.NamedDeBruijn DefaultUni DefaultFun) Term + , [Text] + ) +runTermCek = + (\(res, _, logs) -> (res, logs)) + . runCekDeBruijn PLC.defaultCekParameters Cek.restrictingEnormous Cek.logEmitter + +{- | Evaluate a PLC term and check that the result matches a given Haskell value + (perhaps obtained by running the Haskell code that the term was compiled + from). We evaluate the lifted Haskell value as well, because lifting may + produce reducible terms. The function is polymorphic in the comparison + operator so that we can use it with both HUnit Assertions and QuickCheck + Properties. -} +cekResultMatchesHaskellValue + :: Tx.Lift DefaultUni a + => Term + -> (EvaluationResult Term -> EvaluationResult Term -> b) + -> a + -> b +cekResultMatchesHaskellValue term matches value = + (unsafeRunTermCek term) `matches` (unsafeRunTermCek $ haskellValueToTerm value) diff --git a/plutus-tx/changelog.d/20240516_215552_ana.pantilie95_data_assoclist.md b/plutus-tx/changelog.d/20240516_215552_ana.pantilie95_data_assoclist.md new file mode 100644 index 00000000000..73cd8f8e8af --- /dev/null +++ b/plutus-tx/changelog.d/20240516_215552_ana.pantilie95_data_assoclist.md @@ -0,0 +1,7 @@ +### Added + +- Added `Data.AssocList.Map` module which provides a map implementation based on `Data`. + +### Changed + +- The PlutusTx `These` type had the Haskell implementations of `Show`, `Eq` and `Ord` instances instead of PlutusTx ones. This has been fixed. diff --git a/plutus-tx/plutus-tx.cabal b/plutus-tx/plutus-tx.cabal index fa3513d81f2..0926ed5806f 100644 --- a/plutus-tx/plutus-tx.cabal +++ b/plutus-tx/plutus-tx.cabal @@ -75,6 +75,7 @@ library PlutusTx.Builtins.Internal PlutusTx.Code PlutusTx.Coverage + PlutusTx.Data.AssocMap PlutusTx.Either PlutusTx.Enum PlutusTx.Eq diff --git a/plutus-tx/src/PlutusTx/AssocMap.hs b/plutus-tx/src/PlutusTx/AssocMap.hs index 12cfce5684c..d5c6c800150 100644 --- a/plutus-tx/src/PlutusTx/AssocMap.hs +++ b/plutus-tx/src/PlutusTx/AssocMap.hs @@ -39,7 +39,7 @@ module PlutusTx.AssocMap ( import Prelude qualified as Haskell -import PlutusTx.Builtins qualified as P +import PlutusTx.Builtins qualified as P hiding (null) import PlutusTx.Builtins.Internal qualified as BI import PlutusTx.IsData import PlutusTx.Lift (makeLift) diff --git a/plutus-tx/src/PlutusTx/Builtins.hs b/plutus-tx/src/PlutusTx/Builtins.hs index cfdd8cbe23e..5e9550d9bb6 100644 --- a/plutus-tx/src/PlutusTx/Builtins.hs +++ b/plutus-tx/src/PlutusTx/Builtins.hs @@ -68,6 +68,7 @@ module PlutusTx.Builtins ( -- * Pairs , pairToPair -- * Lists + , null , matchList , matchList' , headMaybe @@ -388,6 +389,10 @@ trace = BI.trace encodeUtf8 :: BuiltinString -> BuiltinByteString encodeUtf8 = BI.encodeUtf8 +{-# INLINABLE null #-} +null :: forall a. BI.BuiltinList a -> Bool +null l = fromOpaque (BI.null l) + {-# INLINABLE matchList #-} matchList :: forall a r . BI.BuiltinList a -> (() -> r) -> (a -> BI.BuiltinList a -> r) -> r matchList l nilCase consCase = BI.chooseList l nilCase (\_ -> consCase (BI.head l) (BI.tail l)) () diff --git a/plutus-tx/src/PlutusTx/Data/AssocMap.hs b/plutus-tx/src/PlutusTx/Data/AssocMap.hs new file mode 100644 index 00000000000..48712bd3274 --- /dev/null +++ b/plutus-tx/src/PlutusTx/Data/AssocMap.hs @@ -0,0 +1,412 @@ +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE ViewPatterns #-} + +module PlutusTx.Data.AssocMap ( + Map, + lookup, + member, + insert, + delete, + singleton, + empty, + null, + toList, + toBuiltinList, + safeFromList, + unsafeFromList, + unsafeFromBuiltinList, + noDuplicateKeys, + all, + any, + union, + unionWith, + ) where + +import PlutusTx.Builtins qualified as P +import PlutusTx.Builtins.Internal qualified as BI +import PlutusTx.IsData qualified as P +import PlutusTx.Lift (makeLift) +import PlutusTx.Prelude hiding (all, any, null, toList, uncons) +import PlutusTx.These + + +import Prelude qualified as Haskell + +{- | A map associating keys and values backed by `P.BuiltinData`. + +This implementation has the following characteristics: + + * The `P.toBuiltinData` and `P.unsafeFromBuiltinData` operations are no-op. + * Other operations are slower than @PlutusTx.AssocMap.Map@, although equality + checks on keys can be faster due to `P.equalsData`. + * Many operations involve converting the keys and\/or values to\/from `P.BuiltinData`. + +Therefore this implementation is likely a better choice than "PlutusTx.AssocMap.Map" +if it is part of a data type defined using @asData@, and the key and value types +have efficient `P.toBuiltinData` and `P.unsafeFromBuiltinData` operations (e.g., they +are primitive types or types defined using @asData@). + +A `Map` is considered well-defined if it has no duplicate keys. Most operations +preserve the definedness of the resulting `Map` unless otherwise noted. +It is important to observe that, in comparison to standard map implementations, +this implementation provides slow lookup and update operations because it is based +on a list representation. +-} +newtype Map k a = Map (BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData)) + deriving stock (Haskell.Eq, Haskell.Show) + +instance P.ToData (Map k a) where + {-# INLINEABLE toBuiltinData #-} + toBuiltinData (Map d) = BI.mkMap d + +instance P.FromData (Map k a) where + {-# INLINABLE fromBuiltinData #-} + fromBuiltinData = Just . Map . BI.unsafeDataAsMap + +instance P.UnsafeFromData (Map k a) where + {-# INLINABLE unsafeFromBuiltinData #-} + unsafeFromBuiltinData = Map . BI.unsafeDataAsMap + +{-# INLINEABLE lookup #-} +-- | Look up the value corresponding to the key. +-- If the `Map` is not well-defined, the result is the value associated with +-- the left-most occurrence of the key in the list. +-- This operation is O(n). +lookup :: forall k a. (P.ToData k, P.UnsafeFromData a) => k -> Map k a -> Maybe a +lookup (P.toBuiltinData -> k) (Map m) = P.unsafeFromBuiltinData <$> lookup' k m + +lookup' + :: BuiltinData + -> BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) + -> Maybe BuiltinData +lookup' k m = go m + where + go xs = + P.matchList + xs + (\() -> Nothing) + ( \hd -> + let k' = BI.fst hd + in if P.equalsData k k' + then \_ -> Just (BI.snd hd) + else go + ) + +{-# INLINEABLE member #-} +-- | Check if the key is in the `Map`. +member :: forall k a. (P.ToData k) => k -> Map k a -> Bool +member (P.toBuiltinData -> k) (Map m) = member' k m + +member' :: BuiltinData -> BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> Bool +member' k = go + where + go :: BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> Bool + go xs = + P.matchList + xs + (\() -> False) + ( \hd -> + let k' = BI.fst hd + in if P.equalsData k k' + then \_ -> True + else go + ) + +{-# INLINEABLE insert #-} +-- | Insert a key-value pair into the `Map`. If the key is already present, +-- the value is updated. +insert :: forall k a. (P.ToData k, P.ToData a) => k -> a -> Map k a -> Map k a +insert (P.toBuiltinData -> k) (P.toBuiltinData -> a) (Map m) = Map $ insert' k a m + +insert' + :: BuiltinData + -> BuiltinData + -> BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) + -> BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) +insert' k a = go + where + go :: + BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> + BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) + go xs = + P.matchList + xs + (\() -> BI.mkCons (BI.mkPairData k a) nil) + ( \hd tl -> + let k' = BI.fst hd + in if P.equalsData k k' + then BI.mkCons (BI.mkPairData k a) tl + else BI.mkCons hd (go tl) + ) + +{-# INLINEABLE delete #-} +-- | Delete a key value pair from the `Map`. +-- If the `Map` is not well-defined, it deletes the pair associated with the +-- left-most occurrence of the key in the list. +delete :: forall k a. (P.ToData k) => k -> Map k a -> Map k a +delete (P.toBuiltinData -> k) (Map m) = Map $ delete' k m + +delete' :: + BuiltinData -> + BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> + BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) +delete' k = go + where + go :: + BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> + BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) + go xs = + P.matchList + xs + (\() -> nil) + ( \hd tl -> + let k' = BI.fst hd + in if P.equalsData k k' + then tl + else BI.mkCons hd (go tl) + ) + +{-# INLINEABLE singleton #-} +-- | Create an `Map` with a single key-value pair. +singleton :: forall k a. (P.ToData k, P.ToData a) => k -> a -> Map k a +singleton (P.toBuiltinData -> k) (P.toBuiltinData -> a) = + Map $ BI.mkCons (BI.mkPairData k a) nil + +{-# INLINEABLE empty #-} +-- | An empty `Map`. +empty :: forall k a. Map k a +empty = Map nil + +{-# INLINEABLE null #-} +-- | Check if the `Map` is empty. +null :: forall k a. Map k a -> Bool +null (Map m) = P.null m + +{-# INLINEABLE safeFromList #-} +-- | Create an `Map` from a list of key-value pairs. +-- In case of duplicates, this function will keep only one entry (the one that precedes). +-- In other words, this function de-duplicates the input list. +safeFromList :: forall k a . (P.ToData k, P.ToData a) =>[(k, a)] -> Map k a +safeFromList = + Map + . toOpaque + . foldr (uncurry go) [] + where + go k v [] = [(P.toBuiltinData k, P.toBuiltinData v)] + go k v ((k', v') : rest) = + if P.toBuiltinData k == k' + then (P.toBuiltinData k, P.toBuiltinData v) : go k v rest + else (P.toBuiltinData k', P.toBuiltinData v') : go k v rest + +{-# INLINEABLE unsafeFromList #-} +-- | Unsafely create an 'Map' from a list of pairs. +-- This should _only_ be applied to lists which have been checked to not +-- contain duplicate keys, otherwise the resulting 'Map' will contain +-- conflicting entries (two entries sharing the same key), and therefore be ill-defined. +unsafeFromList :: (P.ToData k, P.ToData a) => [(k, a)] -> Map k a +unsafeFromList = + Map + . toOpaque + . PlutusTx.Prelude.map (\(k, a) -> (P.toBuiltinData k, P.toBuiltinData a)) + +{-# INLINEABLE noDuplicateKeys #-} +-- | Check if the `Map` is well-defined. Warning: this operation is O(n^2). +noDuplicateKeys :: forall k a. Map k a -> Bool +noDuplicateKeys (Map m) = go m + where + go :: BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> Bool + go xs = + P.matchList + xs + (\() -> True) + ( \hd tl -> + let k = BI.fst hd + in if member k (Map tl) then False else go tl + ) + +{-# INLINEABLE all #-} +--- | Check if all values in the `Map` satisfy the predicate. +all :: forall k a. (P.UnsafeFromData a) => (a -> Bool) -> Map k a -> Bool +all p (Map m) = go m + where + go :: BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> Bool + go xs = + P.matchList + xs + (\() -> True) + ( \hd -> + let a = P.unsafeFromBuiltinData (BI.snd hd) + in if p a then go else \_ -> False + ) + +{-# INLINEABLE any #-} +-- | Check if any value in the `Map` satisfies the predicate. +any :: forall k a. (P.UnsafeFromData a) => (a -> Bool) -> Map k a -> Bool +any p (Map m) = go m + where + go :: BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> Bool + go xs = + P.matchList + xs + (\() -> False) + ( \hd -> + let a = P.unsafeFromBuiltinData (BI.snd hd) + in if p a then \_ -> True else go + ) + +{-# INLINEABLE union #-} + +-- | Combine two 'Map's into one. It saves both values if the key is present in both maps. +union :: + forall k a b. + (P.UnsafeFromData a, P.UnsafeFromData b, P.ToData a, P.ToData b) => + Map k a -> + Map k b -> + Map k (These a b) +union (Map ls) (Map rs) = Map res + where + goLeft xs = + P.matchList + xs + (\() -> nil) + ( \hd tl -> + let k = BI.fst hd + v = BI.snd hd + v' = case lookup' k rs of + Just r -> + P.toBuiltinData + ( These + (P.unsafeFromBuiltinData v) + (P.unsafeFromBuiltinData r) + :: These a b + ) + Nothing -> + P.toBuiltinData (This (P.unsafeFromBuiltinData v) :: These a b) + in BI.mkCons (BI.mkPairData k v') (goLeft tl) + ) + + goRight xs = + P.matchList + xs + (\() -> nil) + ( \hd tl -> + let k = BI.fst hd + v = BI.snd hd + v' = case lookup' k ls of + Just r -> + P.toBuiltinData + ( These + (P.unsafeFromBuiltinData v) + (P.unsafeFromBuiltinData r) + :: These a b + ) + Nothing -> + P.toBuiltinData (That (P.unsafeFromBuiltinData v) :: These a b) + in BI.mkCons (BI.mkPairData k v') (goRight tl) + ) + + res = goLeft ls `safeAppend` goRight rs + + safeAppend xs1 xs2 = + P.matchList + xs1 + (\() -> xs2) + ( \hd tl -> + let k = BI.fst hd + v = BI.snd hd + in insert' k v (safeAppend tl xs2) + ) + +-- | Combine two 'Map's with the given combination function. +unionWith :: + forall k a. + (P.UnsafeFromData a, P.ToData a) => + (a -> a -> a) -> + Map k a -> + Map k a -> + Map k a +unionWith f (Map ls) (Map rs) = + Map res + where + ls' :: BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) + ls' = go ls + where + go xs = + P.matchList + xs + (\() -> nil) + ( \hd tl -> + let k' = BI.fst hd + v' = BI.snd hd + v'' = case lookup' k' rs of + Just r -> + P.toBuiltinData + (f (P.unsafeFromBuiltinData v') (P.unsafeFromBuiltinData r)) + Nothing -> v' + in BI.mkCons (BI.mkPairData k' v'') (go tl) + ) + + rs' :: BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) + rs' = go rs + where + go xs = + P.matchList + xs + (\() -> nil) + ( \hd tl -> + let k' = BI.fst hd + tl' = go tl + in if member' k' ls + then tl' + else BI.mkCons hd tl' + ) + + res :: BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) + res = go rs' ls' + where + go acc xs = + P.matchList + xs + (\() -> acc) + (\hd -> go (BI.mkCons hd acc)) + +{-# INLINEABLE toList #-} +-- | Convert the `Map` to a list of key-value pairs. This operation is O(n). +-- See 'toBuiltinList' for a more efficient alternative. +toList :: (P.UnsafeFromData k, P.UnsafeFromData a) => Map k a -> [(k, a)] +toList d = go (toBuiltinList d) + where + go xs = + P.matchList + xs + (\() -> []) + ( \hd tl -> + (P.unsafeFromBuiltinData (BI.fst hd), P.unsafeFromBuiltinData (BI.snd hd)) + : go tl + ) + +{-# INLINEABLE toBuiltinList #-} +-- | Convert the `Map` to a `P.BuiltinList` of key-value pairs. This operation is O(1). +toBuiltinList :: Map k a -> BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) +toBuiltinList (Map d) = d + +{-# INLINEABLE unsafeFromBuiltinList #-} +-- | Unsafely create an 'Map' from a `P.BuiltinList` of key-value pairs. +-- This function is unsafe because it assumes that the elements of the list can be safely +-- decoded from their 'BuiltinData' representation. +unsafeFromBuiltinList :: + forall k a. + BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) -> + Map k a +unsafeFromBuiltinList = Map + +{-# INLINEABLE nil #-} +-- | An empty `P.BuiltinList` of key-value pairs. +nil :: BI.BuiltinList (BI.BuiltinPair BuiltinData BuiltinData) +nil = BI.mkNilPairData BI.unitval + +makeLift ''Map diff --git a/plutus-tx/src/PlutusTx/Eq.hs b/plutus-tx/src/PlutusTx/Eq.hs index 0e7b921dfad..de6e87926c7 100644 --- a/plutus-tx/src/PlutusTx/Eq.hs +++ b/plutus-tx/src/PlutusTx/Eq.hs @@ -4,6 +4,7 @@ module PlutusTx.Eq (Eq(..), (/=)) where import PlutusTx.Bool import PlutusTx.Builtins qualified as Builtins import PlutusTx.Either (Either (..)) +import PlutusTx.These import Prelude (Maybe (..)) {- HLINT ignore -} @@ -77,3 +78,10 @@ instance Eq () where instance (Eq a, Eq b) => Eq (a, b) where {-# INLINABLE (==) #-} (a, b) == (a', b') = a == a' && b == b' + +instance (Eq a, Eq b) => Eq (These a b) where + {-# INLINABLE (==) #-} + (This a) == (This a') = a == a' + (That b) == (That b') = b == b' + (These a b) == (These a' b') = a == a' && b == b' + _ == _ = False diff --git a/plutus-tx/src/PlutusTx/IsData/Instances.hs b/plutus-tx/src/PlutusTx/IsData/Instances.hs index 0da5b45e979..ad0dbf5b5d4 100644 --- a/plutus-tx/src/PlutusTx/IsData/Instances.hs +++ b/plutus-tx/src/PlutusTx/IsData/Instances.hs @@ -12,12 +12,14 @@ import PlutusTx.Bool (Bool (..)) import PlutusTx.Either (Either (..)) import PlutusTx.IsData.TH (makeIsDataIndexed, unstableMakeIsData) import PlutusTx.Maybe (Maybe (..)) +import PlutusTx.These (These (..)) -- While these types should be stable, we really don't want them changing, so index -- them explicitly to be sure. makeIsDataIndexed ''Bool [('False,0),('True,1)] makeIsDataIndexed ''Maybe [('Just,0),('Nothing,1)] makeIsDataIndexed ''Either [('Left,0),('Right,1)] +makeIsDataIndexed ''These [('This,0),('That,1),('These,2)] -- Okay to use unstableMakeIsData here since there's only one alternative and we're sure -- that will never change. diff --git a/plutus-tx/src/PlutusTx/Lift/Instances.hs b/plutus-tx/src/PlutusTx/Lift/Instances.hs index d5132023e16..98832e7b576 100644 --- a/plutus-tx/src/PlutusTx/Lift/Instances.hs +++ b/plutus-tx/src/PlutusTx/Lift/Instances.hs @@ -18,6 +18,7 @@ import PlutusTx.Bool (Bool (..)) import PlutusTx.Either (Either (..)) import PlutusTx.Lift.TH import PlutusTx.Maybe (Maybe (..)) +import PlutusTx.These (These (..)) -- Standard types -- These need to be in a separate file for TH staging reasons @@ -25,6 +26,7 @@ import PlutusTx.Maybe (Maybe (..)) makeLift ''Bool makeLift ''Maybe makeLift ''Either +makeLift ''These makeLift ''[] makeLift ''() -- include a few tuple instances for convenience diff --git a/plutus-tx/src/PlutusTx/Ord.hs b/plutus-tx/src/PlutusTx/Ord.hs index 210826488df..f92baf2e2bb 100644 --- a/plutus-tx/src/PlutusTx/Ord.hs +++ b/plutus-tx/src/PlutusTx/Ord.hs @@ -11,6 +11,7 @@ import PlutusTx.Bool (Bool (..)) import PlutusTx.Builtins qualified as Builtins import PlutusTx.Either (Either (..)) import PlutusTx.Eq +import PlutusTx.These import Prelude (Maybe (..), Ordering (..)) {- HLINT ignore -} @@ -123,3 +124,17 @@ instance (Ord a, Ord b) => Ord (a, b) where case compare a a' of EQ -> compare b b' c -> c + +instance (Ord a, Ord b) => Ord (These a b) where + {-# INLINABLE compare #-} + compare (This a) (This a') = compare a a' + compare (That b) (That b') = compare b b' + compare (These a b) (These a' b') = + case compare a a' of + EQ -> compare b b' + c -> c + compare (This _) _ = LT + compare (That _) (This _) = GT + compare (That _) (These _ _) = LT + compare (These _ _) (This _) = GT + compare (These _ _) (That _) = GT diff --git a/plutus-tx/src/PlutusTx/Show.hs b/plutus-tx/src/PlutusTx/Show.hs index 5b16c1ebf9c..e8d57caedbc 100644 --- a/plutus-tx/src/PlutusTx/Show.hs +++ b/plutus-tx/src/PlutusTx/Show.hs @@ -25,6 +25,7 @@ import PlutusTx.List (foldr) import PlutusTx.Maybe import PlutusTx.Prelude hiding (foldr) import PlutusTx.Show.TH +import PlutusTx.These instance Show Builtins.Integer where {-# INLINEABLE showsPrec #-} @@ -160,3 +161,4 @@ deriveShow ''(,,,,,,,,,,,,,,,,,,,,,,,,,) deriveShow ''(,,,,,,,,,,,,,,,,,,,,,,,,,,) deriveShow ''Maybe deriveShow ''Either +deriveShow ''These diff --git a/plutus-tx/src/PlutusTx/These.hs b/plutus-tx/src/PlutusTx/These.hs index 4ec6742e344..124a37d6102 100644 --- a/plutus-tx/src/PlutusTx/These.hs +++ b/plutus-tx/src/PlutusTx/These.hs @@ -1,6 +1,9 @@ -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE LambdaCase #-} + {-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -fno-omit-interface-pragmas #-} + module PlutusTx.These( These(..) , these From 794bf9d9a42f9afb13d30d7352fc84c1451bede0 Mon Sep 17 00:00:00 2001 From: zeme-wana <15709674+zeme-wana@users.noreply.github.com> Date: Wed, 22 May 2024 08:47:08 +0200 Subject: [PATCH 10/11] Let nightly.yml accept hedgehog-tests input (#6062) * Let nightly.yml accept hedgehog-tests input * Default tests from 10k to 50k --- .github/workflows/nightly.yml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index c7f14577e29..3dcbec1628e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -3,32 +3,41 @@ name: Nightly Test Suite on: schedule: - cron: 0 0 * * * # daily at midnight + workflow_dispatch: # or manually dispatch the job + inputs: + hedgehog-tests: + description: Numer of tests to run (--hedgehog-tests XXXXX) + required: false + default: "50000" + +env: + HEDGEHOG_TESTS: ${{ github.event.inputs.hedgehog-tests || 50000 }} jobs: nightly-test-suite: + timeout-minutes: 14400 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - - name: Quick Install Nix - uses: cachix/install-nix-action@V27 - with: - extra_nix_config: | - experimental-features = nix-command flakes - accept-flake-config = true + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + - name: Use Magic Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@main + - name: plutus-core-nightly if: always() run: | pushd plutus-core - nix run --no-warn-dirty --accept-flake-config .#plutus-core-test -- --hedgehog-tests 10000 + nix run --no-warn-dirty --accept-flake-config .#plutus-core-test -- --hedgehog-tests $HEDGEHOG_TESTS popd - name: plutus-ir-nightly if: always() run: | pushd plutus-core - nix run --no-warn-dirty --accept-flake-config .#plutus-ir-test -- --hedgehog-tests 10000 + nix run --no-warn-dirty --accept-flake-config .#plutus-ir-test -- --hedgehog-tests $HEDGEHOG_TESTS popd From 5771700610196202058c9cacd327837c51765106 Mon Sep 17 00:00:00 2001 From: zeme-wana <15709674+zeme-wana@users.noreply.github.com> Date: Wed, 22 May 2024 14:18:49 +0200 Subject: [PATCH 11/11] Remove obsolete defer-plugin-errors flag (#6080) --- doc/read-the-docs-site/plutus-doc.cabal | 9 --------- 1 file changed, 9 deletions(-) diff --git a/doc/read-the-docs-site/plutus-doc.cabal b/doc/read-the-docs-site/plutus-doc.cabal index 1c31b3d75b1..86f51515c0c 100644 --- a/doc/read-the-docs-site/plutus-doc.cabal +++ b/doc/read-the-docs-site/plutus-doc.cabal @@ -17,13 +17,6 @@ source-repository head type: git location: https://github.com/IntersectMBO/plutus -flag defer-plugin-errors - description: - Defer errors from the plugin, useful for things like Haddock that can't handle it. - - default: False - manual: True - common lang default-language: Haskell2010 default-extensions: @@ -47,8 +40,6 @@ common lang -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas - if flag(defer-plugin-errors) - common ghc-version-support -- See the section on GHC versions in CONTRIBUTING if (impl(ghc <9.6) || impl(ghc >=9.7))