diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b2aefbc3adb..57191a80ba9 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -245,7 +245,7 @@ generate-malonzo-code # Available in Nix shell ### Important Files - **cabal.project** - Main project configuration -- **CONTRIBUTING.adoc** - Detailed contribution guidelines +- **CONTRIBUTING.md** - Detailed contribution guidelines - **scripts/** - Build and utility scripts - **.github/workflows/** - CI/CD pipeline definitions diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.md similarity index 66% rename from CONTRIBUTING.adoc rename to CONTRIBUTING.md index be2d69e6a61..6ebd04d30fc 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.md @@ -1,8 +1,6 @@ -= Contributing to Plutus -:toc: left -:reproducible: +# Contributing to Plutus -== Setting up and working with our development tools +## Setting up and working with our development tools This project relies on Nix to provision the complete toolchain needed to build all repository artifacts. @@ -10,15 +8,15 @@ Thanks to Nix we ensure that everyone has consistent versions of the tools that Please use Nix since problems due to mismatched versions of tools are particularly annoying to fix! -If you _really_ cannot use Nix and still want to contribute to Plutus then xref:develop-without-nix[skip to the relevant section]. +If you _really_ cannot use Nix and still want to contribute to Plutus then [skip to the relevant section](#how-to-develop-without-nix). -=== Installing and setting up Nix +### Installing and setting up Nix -For instructions on how to install and configure nix (including how to enable access to our binary caches), refer to link:https://github.com/input-output-hk/iogx/blob/main/doc/nix-setup-guide.md[this document]. +For instructions on how to install and configure nix (including how to enable access to our binary caches), refer to [this document](https://github.com/input-output-hk/iogx/blob/main/doc/nix-setup-guide.md). If you already have nix installed and setup, you may enter the development shell by running `nix develop`. -=== Note on pre-commit hooks +### Note on pre-commit hooks If you are committing code outside `nix develop`, you will get this error: ``` @@ -28,83 +26,82 @@ In that case, you may either pass the flag `--no-verify` to `git commit`, or `pi The `pre-commit` checks will be run by CI anyway, so any formatting errors will be caught when submitting your PR. -=== What to do once inside the `nix develop` shell +### What to do once inside the `nix develop` shell Your prompt will change to `[plutus-shell]` and you will be presented with a menu of available commands. Please read that menu carefully. -=== How to build the code during development +### How to build the code during development The `nix develop` environment has the correct GHC with all the external Haskell dependencies of the project. From here you can build the project packages directly with `cabal`. -NOTE: You may need to run `cabal update` so that `cabal` knows about the index state xref:update-haskell-deps[we have pinned]. +**📌 NOTE**\ +You may need to run `cabal update` so that `cabal` knows about the index state [we have pinned](#how-to-update-our-haskell-dependencies). Run `cabal build plutus-core` from the root to build the Plutus Core library. -See the link:./cabal.project[cabal project file] for a list of other packages that you can build with `cabal`. +See the [cabal project file](./cabal.project) for a list of other packages that you can build with `cabal`. -=== How to setup `haskell-language-server` +### How to setup `haskell-language-server` The `nix develop` environment has a `haskell-language-server` binary for the right version of GHC. -IMPORTANT: This binary is called `haskell-language-server`, rather than `haskell-language-server-wrapper`, which is what some of the editor integrations expect. +**❗ IMPORTANT**\ +This binary is called `haskell-language-server`, rather than `haskell-language-server-wrapper`, which is what some of the editor integrations expect. -We don't have a `hie.yaml`, the implicit cradle support in HLS seems to work fine these days. +We don’t have a `hie.yaml`, the implicit cradle support in HLS seems to work fine these days. -[[build-with-nix]] -=== How to build the project's artifacts with Nix +### How to build the project’s artifacts with Nix -Haskell components are provisioned by Nix via link:https://github.com/input-output-hk/haskell.nix[Haskell.nix] +Haskell components are provisioned by Nix via [Haskell.nix](https://github.com/input-output-hk/haskell.nix) In general you can run `nix build .#cabalProject.SYSTEM.hsPkgs.PACKAGE.components.COMPONENT` For example `nix build .#cabalProject.x86_64-linux.hsPkgs.plutus-core.components.library` -For full documentation about the Nix code see the link:nix/README.md[Nix README]. +For full documentation about the Nix code see the [Nix README](nix/README.md). There you will find how to build all other artifacts, which are mostly related to documentation. -[[develop-without-nix]] -=== How to develop without Nix +### How to develop without Nix -You can build some of the Haskell packages without Nix, but this is not recommended and we don't guarantee that these prerequisites are sufficient. +You can build some of the Haskell packages without Nix, but this is not recommended and we don’t guarantee that these prerequisites are sufficient. -If you use Nix, these tools are provided for you via `nix develop`, and you do *not* need to install them yourself. +If you use Nix, these tools are provided for you via `nix develop`, and you do **not** need to install them yourself. -* If you want to build our Agda code, then install https://github.com/agda/agda[Agda] and the https://github.com/agda/agda-stdlib[standard library]. -* If you want to build our Haskell packages with https://www.haskell.org/cabal/[`cabal`], then install it. +* If you want to build our Agda code, then install [Agda](https://github.com/agda/agda) and the [standard library](https://github.com/agda/agda-stdlib). +* If you want to build our Haskell packages with [`cabal`](https://www.haskell.org/cabal/), then install it. + +
⚠️ WARNING
-[WARNING] -==== * We rely on forked or new versions of some system libraries. -** You can read the https://developers.cardano.org/docs/operate-a-stake-pool/node-operations/installing-cardano-node/[Cardano node documentation] to find out how to install these. + * You can read the [Cardano node documentation](https://developers.cardano.org/docs/operate-a-stake-pool/node-operations/installing-cardano-node/) to find out how to install these. * You may get different versions of packages. -** This *shouldn't* happen, but we can't guarantee it. + * This **shouldn’t** happen, but we can’t guarantee it. * We are not currently enabling the Nix integration for these tools, so they will use your system GHC and libraries, rather than that ones that will be used by Nix. -** We sometimes patch the GHC that we use in Nix, so -this can at least potentially cause problems or cause you to be missing -bug workarounds. -==== + * We sometimes patch the GHC that we use in Nix, so + this can at least potentially cause problems or cause you to be missing + bug workarounds. +
-=== How to add a new Haskell package +### How to add a new Haskell package You need to do a few things when adding a new package, in the following order: -- Add the cabal file for the new package. -- Add the package to link:cabal.project[`cabal.project`]. -- Check that you can build the package with nix as well (see xref:build-with-nix[How to build with Nix]) or wait for CI to check this for you. +* Add the cabal file for the new package. +* Add the package to [`cabal.project`](cabal.project). +* Check that you can build the package with nix as well (see [How to build with Nix](#how-to-build-the-projects-artifacts-with-nix)) or wait for CI to check this for you. -[[update-haskell-deps]] -=== How to update our Haskell dependencies +### How to update our Haskell dependencies Our Haskell packages come from two package repositories: - Hackage -- https://github.com/IntersectMBO/cardano-haskell-packages[CHaP] (which is essentially another Hackage) +- [CHaP](https://github.com/IntersectMBO/cardano-haskell-packages) (which is essentially another Hackage) The "index state" of each repository is pinned to a particular time in `cabal.project`. This tells Cabal to treat the repository "as if" it was the specified time, ensuring reproducibility. @@ -115,74 +112,73 @@ That typically just means that we need to fix the breakage (and add a lower-boun Note that `cabal` itself keeps track of what index states it knows about, so when you bump the pinned index state you may need call `cabal update` in order for `cabal` to be happy. The Nix code which builds our packages also cares about the index state. -This is represented by some pinned inputs in our flake (see xref:update-nix-pins[here] for more details) +This is represented by some pinned inputs in our flake (see [here](#how-to-update-our-pinned-nix-dependencies) for more details) You can update these by running: - `nix flake update hackage` for Hackage - `nix flake update CHaP` for CHaP -==== Use of `source-repository-package`s +#### Use of `source-repository-package`s -We *can* use Cabal's `source-repository-package` mechanism to pull in un-released package versions. +We **can** use Cabal’s `source-repository-package` mechanism to pull in un-released package versions. However, we should try and avoid this. In particular, we should not release our packages while we depend on a `source-repository-package`. -If we are stuck in a situation where we need a long-running fork of a package, we should release it to CHaP instead (see the https://github.com/IntersectMBO/cardano-haskell-packages[CHaP README] for more). +If we are stuck in a situation where we need a long-running fork of a package, we should release it to CHaP instead (see the [CHaP README](https://github.com/IntersectMBO/cardano-haskell-packages) for more). If you do add a `source-repository-package`, you need to update the `sha256` mapping in `nix/project.nix`. For the moment you have to do this by hand, using the following command to get the sha: `nix-prefetch-git --quiet | jq .sha256`, or by just getting it wrong and trying to build it, in which case Nix will give you the right value. -[[update-nix-pins]] -=== How to update our pinned Nix dependencies +### How to update our pinned Nix dependencies We pin versions of some git repositories that are used by Nix, for example `nixpkgs`. -For documentation see https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html#flake-inputs[the Nix flake inputs documentation] -and https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake-lock.html[the Nix flake lock command]. +For documentation see [the Nix flake inputs documentation](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html#flake-inputs) +and [the Nix flake lock command](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake-lock.html). Specifically, you will probably want to say `nix flake update `. -Do *not* use `nix flake update`, as that will update all the inputs, which we typically don't want to do. +Do **not** use `nix flake update`, as that will update all the inputs, which we typically don’t want to do. -=== How to build the code with profiling +### How to build the code with profiling TODO: Currently not available, coming soon If you launch `nix develop .#profiled` you will get a shell where all the dependencies have been built with profiling. -[WARNING] -==== +
⚠️ WARNING
+ The shell with profiling dependencies is not currently cached, so this will result in you rebuilding all of our dependencies with profiling on your machine. -This will take a *long* time. -==== +This will take a **long** time. +
-Once you have a shell with profiling libraries for our dependencies, add `profiling: true` to `cabal.project.local`, which will tell cabal that you want profiling (in particular, that will cause it to build *our* libraries with profiling). +Once you have a shell with profiling libraries for our dependencies, add `profiling: true` to `cabal.project.local`, which will tell cabal that you want profiling (in particular, that will cause it to build **our** libraries with profiling). -Alternatively, you can pass the `--enable-profiling` option to `cabal` on an ad-hoc basis, but adding the option to `cabal.project.local` will make it apply to everything, which is probably what you want when you're doing profiling work. +Alternatively, you can pass the `--enable-profiling` option to `cabal` on an ad-hoc basis, but adding the option to `cabal.project.local` will make it apply to everything, which is probably what you want when you’re doing profiling work. At this point you need to configure which cost centres you want GHC to insert. -The https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/profiling.html[GHC user guide] explains this very well. +The [GHC user guide](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/profiling.html) explains this very well. A typical way of doing this is to add `-fprof-auto` to either the `ghc-options` in the `.cabal` file for the project, or in an `OPTIONS_GHC` pragma in the module you care about. -[WARNING] -==== -Do *not* set the `-prof` option yourself! +
⚠️ WARNING
+ +Do **not** set the `-prof` option yourself! This will enable profiling libraries unconditionally, which interferes with what `cabal` wants. Setting `profiling: true` already sorts this out properly. -==== +
Then you can use the RTS `-p` option to dump a profile e.g. `cabal run plc ... -- +RTS -p`. -[WARNING] -==== +
⚠️ WARNING
+ When building `plutus-core`, you might get a compilation error similar to the following: ``` ghc: ^^ Could not load 'recursionzmschemeszm5zi2zm8KxPjFseRtMJfccAAVODSC_DataziFunctorziFoldableziTH_zdfMakeBaseFunctorNamezuzdcmakeBaseFunctor_closure', dependency unresolved. See top entry above. ByteCodeLink.lookupCE -During interactive linking, GHCi couldn't find the following symbol: +During interactive linking, GHCi couldn’t find the following symbol: recursionzmschemeszm5zi2zm8KxPjFseRtMJfccAAVODSC_DataziFunctorziFoldableziTH_zdfMakeBaseFunctorNamezuzdcmakeBaseFunctor_closure ``` @@ -195,11 +191,11 @@ package plutus-core This issue is tracked upstream at https://gitlab.haskell.org/ghc/ghc/-/issues/18320 -==== +
There are various tools for visualizing the resulting profile, e.g. https://hackage.haskell.org/package/ghc-prof-flamegraph. -== GHC versions +## GHC versions We have a set of supported GHC major versions, which are built in CI and which we commit to keeping working. @@ -215,10 +211,10 @@ At the moment, our supported GHC versions are: - 9.12 - 9.14 -=== Plugin support +### Plugin support Making `plutus-tx-plugin` work on multiple GHC versions can be painful, because it uses a lot of GHC internals. -Since the plugin is not in the dependency closure of the Cardano node, we don't _need_ to support all the GHC versions, which is helpful. +Since the plugin is not in the dependency closure of the Cardano node, we don’t _need_ to support all the GHC versions, which is helpful. We mostly commit to supporting our primary development version, but we _may_ support other versions as well if it is easy. The plugin and its dependents should be marked as unbuildable with cabal conditionals on any non-supported major GHC versions. @@ -228,27 +224,27 @@ We currently support the plugin on: - 9.6 - 9.12 -=== Per-version tooling +### Per-version tooling We have a dev shell for each supported GHC version which you can use if you need to fix issues on that specific version. You can access them like this: `nix develop .#ghc912`. -Note that HLS in particular won't work in the non-primary dev shell as it will still be built with the primary GHC version, but other tooling should work fine. +Note that HLS in particular won’t work in the non-primary dev shell as it will still be built with the primary GHC version, but other tooling should work fine. -=== Per-version testing +### Per-version testing As far as we know, given an exact GHC compiler version the GHC compiler will generate the same (reproducible) GHC-Core, from any Haskell code, running under any platform (linux/osx/windows/etc). However, when comparing the GHC-Core output coming from different GHC versions, this might not still hold. Because the PlutusTx language heavily relies on GHC-Core, any (slight) GHC-Core differences will change the resulting plutus core (and its archetypes, plutus ir and typed plutus core). -Since our (test) code must be reproducible with multiple GHC versions (see xref:plugin-support[Plugin Support]) _at the same time_ in the repository, +Since our (test) code must be reproducible with multiple GHC versions (see [Plugin Support](#plugin-support)) _at the same time_ in the repository, we keep multiple test output directories (containg golden plutus files), one for each GHC version that the plugin supports. To test and/or regenerate test golden test output, one has to run _for each_ GHC version supported by the plugin: ``` -nix develop ".#ghc" --command cabal test plutus-tx-plugin --test-options=--accept +nix develop ".#ghc<version>" --command cabal test plutus-tx-plugin --test-options=--accept ``` where `` is the GHC major version with the decimal points removed (eg @@ -256,16 +252,16 @@ where `` is the GHC major version with the decimal points removed (eg something similar. You can also just type `nix develop .#ghc` to get a nix shell with the appropriate version of GHC. -== Working conventions +## Working conventions -=== Style guide +### Style guide -Please follow our link:STYLEGUIDE{outfilesuffix}[Haskell style guide], which documents most of our conventions for working on Haskell code. +Please follow our [Haskell style guide](STYLEGUIDE.md), which documents most of our conventions for working on Haskell code. -=== Code is communication +### Code is communication We are a relatively large team working on sometimes quite abstruse problems. -As such, it's important that future people who work on the project know how things work, and just as importantly, why. +As such, it’s important that future people who work on the project know how things work, and just as importantly, why. These future people may even be yourself - we forget things very quickly! When writing, try to put yourself in the position of someone coming to this code for the first time. @@ -275,10 +271,10 @@ Write it down! Code review is a good lens for this: if you have to explain something to a reviewer, then it is probably not clear in the code and should have a note. This applies both to the code itself (structure, naming, etc.) and also to comments. -How to write useful comments is a large topic which we don't attempt to cover here, but link:http://antirez.com/news/124[Antirez] is good. +How to write useful comments is a large topic which we don’t attempt to cover here, but [Antirez](http://antirez.com/news/124) is good. If in doubt: write more! -==== "Notes" +#### "Notes" One special kind of comment is worth drawing attention to. We adopt a convention (stolen from GHC) of writing fairly substantial notes in our code with a particular structure. @@ -292,7 +288,7 @@ The structure is: For example: ----- +``` {- Note [How to write a note] A note should look a bit like this. @@ -303,13 +299,13 @@ A ----> B >> C And of course, you should see Note [Another note]. -} ----- +``` Notes are a great place to put substantial discussion that you need to refer to from multiple places. For example, if you used an encoding trick to fit more data into an output format, you could write a Note describing the trick (and justifying its usage!), and then refer to it from the encoder and the decoder. -=== Code formatting +### Code formatting We use `stylish-haskell` for Haskell code formatting, and `cabal-fmt` for cabal files. They are run automatically as pre-commit hooks, but CI will run them again and expect that to be a no-op, so if you somehow don’t apply them your PR will not go green. @@ -318,51 +314,51 @@ To run `stylish-haskell` or `cabal-fmt` manually over your tree, type `pre-commi Without the `--all-files` flag, `pre-commit` will only run on the files that have changed since the last commit. -=== Code linting +### Code linting There are two `.hlint.yaml` files, one in `./` and the other in `.github/`. The one in `./` is the default hint file used by editors, and the one in `.github/` is used by CI. Think of the former as suggested hints, and the latter as enforced hints. -=== Compiler warnings +### Compiler warnings The CI builds Haskell code with `-Werror`, so will fail if there are any compiler warnings. So fix your own warnings! -If the warnings are stupid, we can turn them off, e.g. sometimes it makes sense to add `-Wno-orphans` to a file where we know it's safe. +If the warnings are stupid, we can turn them off, e.g. sometimes it makes sense to add `-Wno-orphans` to a file where we know it’s safe. -=== Commit messages +### Commit messages Please make informative commit messages! -It makes it much easier to work out why things are the way they are when you're debugging things later. +It makes it much easier to work out why things are the way they are when you’re debugging things later. A commit message is communication, so as usual, put yourself in the position of the reader: what does a reviewer, or someone reading the commit message later need to do their job? Write it down! -It is even better to include this information in the code itself, but sometimes it doesn't belong there (e.g. ticket info). +It is even better to include this information in the code itself, but sometimes it doesn’t belong there (e.g. ticket info). Also, include any relevant meta-information, such as ticket numbers. -If a commit completely addresses a ticket, you can put that in the headline if you want, but it's fine to just put it in the body. +If a commit completely addresses a ticket, you can put that in the headline if you want, but it’s fine to just put it in the body. -There is plenty to say on this topic, but broadly the guidelines in link:https://chris.beams.io/posts/git-commit/[this post] are good. +There is plenty to say on this topic, but broadly the guidelines in [this post](https://chris.beams.io/posts/git-commit/) are good. -=== Commit signing +### Commit signing -Set it up if you can, it's relatively easy to do. +Set it up if you can, it’s relatively easy to do. -== Making and reviewing changes +## Making and reviewing changes -=== Changelogs +### Changelogs We write changelogs for our versioned and published packages, which currently include: -- plutus-core -- plutus-ledger-api -- plutus-tx -- plutus-tx-plugin +* plutus-core +* plutus-ledger-api +* plutus-tx +* plutus-tx-plugin -To do this we use https://github.com/nedbat/scriv[`scriv`], a changelog management tool. +To do this we use [`scriv`](https://github.com/nedbat/scriv), a changelog management tool. -==== When to write a changelog entry +#### When to write a changelog entry We have no clear policy here, it is up to the judgement of the contributor. Not all PRs need changelog entries. @@ -371,22 +367,22 @@ However our CI will check for changelog entries unless the 'No Changelog Require The broad heuristic is to put yourself in the position of the consumer of the piece of software in question and ask if you would want to know about this change. If the answer is yes, then write a quick changelog entry. -==== How to write a changelog entry +#### How to write a changelog entry The basic idea is that you write a changelog "fragment" in the `changelog.d` directory. When we do a release, these will be collected into the main `CHANGELOG.md`. -Usually we don't edit `CHANGELOG.md` directly. +Usually we don’t edit `CHANGELOG.md` directly. You can make a changelog fragment using `scriv create` in the package directory, but you can also just create the fragment directly with an editor. A fragment is a markdown file beginning with a header giving the category of change. Currently these are: -- Removed -- Added -- Changed -- Deprecated -- Fixed -- Security +* Removed +* Added +* Changed +* Deprecated +* Fixed +* Security We can change these categories if we want. @@ -403,36 +399,36 @@ Updated foo to take a bar. Deprecated baz. ``` -=== Opening a pull request +### Opening a pull request A pull request is a change to the codebase, but it is also an artifact which goes through a change acceptance process. There are a bunch of things which we can do to make this process smooth which may have nothing to do with the code itself. The key bottleneck in getting a PR merged is code review. -Code review is great (see below), but it can slow you down if you don't take the time to make it easy. +Code review is great (see below), but it can slow you down if you don’t take the time to make it easy. -The amount of time it's worth spending doing this is probably much more than you think. +The amount of time it’s worth spending doing this is probably much more than you think. -==== What branch to target +#### What branch to target PRs should target `master` unless there is a very good reason not to. The only PRs to release branches should be backport PRs which should consist only of cherry-picks of commits from master (and any fixups that are needed). -For more details, see link:./RELEASE{outfilesuffix}[Plutus Release Process]. +For more details, see [Plutus Release Process](./RELEASE.md). -==== What changes to include, and pull request sizes +#### What changes to include, and pull request sizes When developing a non-trivial new feature, usually the best way to get the code reviewed is to break the implementation down to a chain of small diffs, each representing a meaningful, logical and reviewable step. -Unfortunately GitHub doesn't have good support for this. +Unfortunately GitHub doesn’t have good support for this. You basically have three options: -- Open the first PR against master, the second PR against the first PR's branch, and so on. +* Open the first PR against master, the second PR against the first PR’s branch, and so on. Merging a stack of PRs created this way into master can be non-trivial. -- Wait until one PR is merged before opening the next PR. -- Use a single PR for the whole feature that contains multiple small commits. - The problem is that Github doesn't support approving, rejecting or merging individual commits in a PR. - You can look at each individual commit, but it's not necessarily useful or even appropriate - many PRs have quite messy commits, and commits are sometimes overwritten via force push. +* Wait until one PR is merged before opening the next PR. +* Use a single PR for the whole feature that contains multiple small commits. + The problem is that Github doesn’t support approving, rejecting or merging individual commits in a PR. + You can look at each individual commit, but it’s not necessarily useful or even appropriate - many PRs have quite messy commits, and commits are sometimes overwritten via force push. -The first two options are often referred to as link:https://trunkbaseddevelopment.com/[trunk-based development], while the third "long-lived feature branches". +The first two options are often referred to as [trunk-based development](https://trunkbaseddevelopment.com/), while the third "long-lived feature branches". There is no single best option for all cases, although in general we encourage adopting trunk-based development styles. Long-lived feature branches with too many commits are harmful because @@ -445,31 +441,31 @@ You can simply not turn them on until they are ready, or guard them with conditi But this is not a hard rule and should be determined on a case-by-case basis. Sometimes for a small or medium-sized piece of work, you may not want to break it into multiple PRs, and wait till each PR is merged before creating the next one. -You'd rather put all your code out quickly in a single PR for review. -And that's fine. -Or maybe it's a piece of performance improvement work, and you don't know whether or not it actually improves the performance, until you finish implementing and testing the whole thing. +You’d rather put all your code out quickly in a single PR for review. +And that’s fine. +Or maybe it’s a piece of performance improvement work, and you don’t know whether or not it actually improves the performance, until you finish implementing and testing the whole thing. Whichever option you choose, please keep each of your PR to a single topic. Do not mix business logic with such things as reformatting and refactoring in a single PR. -==== Pull request descriptions +#### Pull request descriptions A pull request is communication, so as usual, put yourself in the position of the reader: what does your audience (the reviewer) need to know to do their job? This information is easy for you to access, but hard for them to figure out, so write it down! However, better to put information in the code, commit messages, or changelog if possible: these persist but PR descriptions do not. -It's okay to repeat information from such places, or simply to point to it. -For one-commit PRs, Github will automatically populate the PR description with the commit message, so if you've written a good commit message you're done! -Sometimes there is "change-related" information that doesn't belong in a commit message but is useful ("Kris I think this will fix the issue you had yesterday"). +It’s okay to repeat information from such places, or simply to point to it. +For one-commit PRs, Github will automatically populate the PR description with the commit message, so if you’ve written a good commit message you’re done! +Sometimes there is "change-related" information that doesn’t belong in a commit message but is useful ("Kris I think this will fix the issue you had yesterday"). -==== Misc PR tips +#### Misc PR tips * Review the diff of your own PR at the last minute before hitting "create". -It's amazing how many obvious things you spot here, and it stops the reviewer having to point them all out. -* It's fine to make WIP PRs if you just want to show your code to someone else or have the CI check it. +It’s amazing how many obvious things you spot here, and it stops the reviewer having to point them all out. +* It’s fine to make WIP PRs if you just want to show your code to someone else or have the CI check it. Use the Github "draft" feature for this. -=== Rebasing and force pushing +### Rebasing and force pushing Force pushing to master (or any other protected branch) is never allowed. There is no exception to this rule. @@ -480,58 +476,58 @@ Indeed, if you need to update your branch with changes from master, rebasing is Some projects do not allow force pushing to any remote branch. This is not a popular policy and we do not adopt it, because -- This means you must only ever use the "merge commit" merge method (or occasionally, fast forward merge, which GitHub doesn't support). -- This means you aren't even allowed to clean up commits in your own PR, and must eventually merge everything into master. +* This means you must only ever use the "merge commit" merge method (or occasionally, fast forward merge, which GitHub doesn’t support). +* This means you aren’t even allowed to clean up commits in your own PR, and must eventually merge everything into master. It discourages people from pushing commits frequently when developing. We should instead _encourage_ cleaning up commits in PRs, at least before merging. -- The argument that this will cause massive pain for those who merge other people's PR branch into their branch is questionable. +* The argument that this will cause massive pain for those who merge other people’s PR branch into their branch is questionable. This should be rare to begin with, if we adopt trunk-based development in general, instead of long-lived feature branches. - And even if you do need to depend on other people's unmerged work, you can instead rebase your branch on theirs, and if their branch changes, just rebase again. + And even if you do need to depend on other people’s unmerged work, you can instead rebase your branch on theirs, and if their branch changes, just rebase again. Rebasing and force pushing can be used to your advantage, for example: * Add low-effort or WIP commits to fix review comments, and then squash them away before merging the PR. -* If you have already had a PR review, don't rebase away the old commits until the PR is ready to merge, so that the reviewer only has to look at the "new" commits. +* If you have already had a PR review, don’t rebase away the old commits until the PR is ready to merge, so that the reviewer only has to look at the "new" commits. * Rewrite the commits to make the story clearer where possible. It is advisable to always prefer `git push --force-with-lease` instead of `git push --force` to ensure that no work gets accidentally deleted. -=== Code review +### Code review All pull-requests should be approved by at least one other person. -We don't enforce this, though: a PR fixing a typo is fine to self-merge, beyond that use your judgement. +We don’t enforce this, though: a PR fixing a typo is fine to self-merge, beyond that use your judgement. As an author, code review is an opportunity for you to get feedback from clear eyes. As a reviewer, code review is an opportunity for you to help your colleagues and learn about what they are doing. Make the best use of it you can! -==== For the author +#### For the author * Pick the right reviewer(s). -If you don't know who to pick, ask! +If you don’t know who to pick, ask! * Respect your reviewers' time. -Their time is as valuable as yours, and it's typically more efficient for you to spend time explaining or clarifying something in advance than for them to puzzle it out or pose a question. -* If someone had to ask about your code, it wasn't clear enough so change it or add a comment. +Their time is as valuable as yours, and it’s typically more efficient for you to spend time explaining or clarifying something in advance than for them to puzzle it out or pose a question. +* If someone had to ask about your code, it wasn’t clear enough so change it or add a comment. Read this blog post for more good tips: https://mtlynch.io/code-review-love/ -==== For the reviewer +#### For the reviewer * Respond to review requests as quickly as you can. -If you can't review it all, say what you can and come back to it. +If you can’t review it all, say what you can and come back to it. Waiting for review is often a blocker for other people, so prioritize it. -* If you don't understand something, ask. -You are as clever as any person who will read this in the future, if it confuses you it's confusing. +* If you don’t understand something, ask. +You are as clever as any person who will read this in the future, if it confuses you it’s confusing. * Do spend the time to understand the code. This will help you make more useful comments, help you review future changes more easily, and help you if you ever need to work on it yourself. * More reviewing is usually helpful. -If you think a PR is interesting, you can review it even if nobody asked you to, you will probably have things to contribute and you'll learn something. +If you think a PR is interesting, you can review it even if nobody asked you to, you will probably have things to contribute and you’ll learn something. Read these blog posts for more good tips: - https://mtlynch.io/human-code-reviews-1/ - https://mtlynch.io/human-code-reviews-2/ -=== Benchmarking PRs +### Benchmarking PRs Sometimes it is useful to benchmark a PR, and we have some automation for this. To trigger it, make a comment on the PR with this form: `/benchmark `, where `` is as you would provide it to cabal. @@ -545,9 +541,9 @@ The job will: 3. Compare the two runs. 4. Post the comparison as a comment to the PR. -=== Merging PRs +### Merging PRs -==== Merge method and commit history +#### Merge method and commit history All 3 Github merge methods (merge commit, squash and merge, and rebase and merge) are allowed. Use whichever you deem appropriate. @@ -556,7 +552,7 @@ The best merge method is different for different cases. That being said, there are not many cases where "rebase and merge" is appropriate, and you might as well rebase it yourself. And if you use this method, your PR must have a clean commit history: every commit should have a meaningful message, and should be buildable. -You don't want to have commits like "fix a typo", "this may work" or "wip, done for the day" in master with a linear history. +You don’t want to have commits like "fix a typo", "this may work" or "wip, done for the day" in master with a linear history. And if some of these commits are non-buildable, it can create problems for "git bisect". This is slightly less of a problem when you use the "merge commit" method. @@ -564,61 +560,62 @@ While these interim commits would still be unpleasant, at least the merge commit The best thing to do, of course, is to not have those interim commits. If you think merging multiple commits makes more sense, clean up the history. -If you don't, squash. The option chosen can vary from PR to PR. +If you don’t, squash. The option chosen can vary from PR to PR. -==== Beware divergence of master and PR branch +#### Beware divergence of master and PR branch Merging a PR can break master, if the PR branch has diverged from master, even if CI on the PR is green. This happens because the PRs conflict in a way that isn’t obvious to git, e.g. one adds a usage of a function and the other removes that function. The problems with a broken master include inconveniencing other developers, and causing problems for "git bisect". -There are ways to guarantee master never breaks, such as GitHub's link:https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue[merge queue]. +There are ways to guarantee master never breaks, such as GitHub’s [merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue). -We don't use the merge queue because +We don’t use the merge queue because -- A broken master has historically been quite infrequent. -- The merge queue increases the time it takes to merge a PR, which causes productivity loss if you are waiting to create the next PR after merging the current one (which happens often). +* A broken master has historically been quite infrequent. +* The merge queue increases the time it takes to merge a PR, which causes productivity loss if you are waiting to create the next PR after merging the current one (which happens often). However, if your PR branch has diverged too much from master, it is recommended that you rebase or merge master into the PR branch before merging. And whenever you notice a broken master, please fix it ASAP. -== External contributors +## External contributors The Plutus team welcomes contributions from external contributors. -However, it can be difficult for the Plutus team to quickly review contributions from people where we don't have an existing relationship. +However, it can be difficult for the Plutus team to quickly review contributions from people where we don’t have an existing relationship. For that reason, we ask you to follow these additional guidelines (the rest of the document also applies!), which will make it easier for us to review your work, and therefore make the contributing process smoother for you. -=== All changes must have an issue +### All changes must have an issue Make sure that any change you make has a corresponding GitHub issue. The issue should describe the problem and describe your proposed solutiion. Before you start working on implementing it, you must get a comment from the Plutus team that the solution seems sensible. This functions as a light "design review" before you get too stuck into doing a PR. -Reviewing the issue makes things easier for the Plutus team (it's easier to read an issue than a PR); and less frustrating for the contributor (it's nicer to get design feeback *before* you have done lots of work on the implementation). -We can also offer advice on implementation, or let you know that we're already planning to fix the issue (or that there is a good reason not to!). +Reviewing the issue makes things easier for the Plutus team (it’s easier to read an issue than a PR); and less frustrating for the contributor (it’s nicer to get design feeback **before** you have done lots of work on the implementation). +We can also offer advice on implementation, or let you know that we’re already planning to fix the issue (or that there is a good reason not to!). -=== Keep changes well-scoped +### Keep changes well-scoped Try to keep your PR focussed on one change. -This is a pratice we try to follow generally, but especially for external contributions where reviews tend to be more laborious, it's good to keep things focussed. -If your PR contains a dozen drive-by refactorings, it's unlikely to be merged as such! +This is a pratice we try to follow generally, but especially for external contributions where reviews tend to be more laborious, it’s good to keep things focussed. +If your PR contains a dozen drive-by refactorings, it’s unlikely to be merged as such! -== Supporting systems +## Supporting systems -=== Continuous integration +### Continuous integration We have two sources of CI checks at the moment: -- Hydra -- Github Actions +* Hydra +* Github Actions The CI will report statuses on your PRs with links to the logs in case of failure. Pull requests cannot be merged without at least the Hydra CI check being green. -NOTE: This isn't strictly true: repository admins (notably Lorenzo) can force-merge PRs without the checks being green +**📌 NOTE**\ +This isn’t strictly true: repository admins (notably Lorenzo) can force-merge PRs without the checks being green If you really need this, ask. -==== Hydra +#### Hydra Hydra is the "standard" CI builder for Nix-based projects. It builds everything in the project, including all the tests, documentation, etc. @@ -627,31 +624,31 @@ Hydra builds jobs based on the `hydraJobs` flake output. Hydra can be a bit flaky, unfortunately: - If evaluation fails saying "out of memory" or "unexpected EOF reading line", then this is likely a transient failure. -These will be automatically retried, but if you're in a hurry Lorenzo has permissions to force a new evaluation. +These will be automatically retried, but if you’re in a hurry Lorenzo has permissions to force a new evaluation. - If a build fails spuriously, this is a _problem_: please report it to whoever is responsible for that build and we should try and iron it out. Nondeterministic failures are very annoying. Lorenzo also has permissions to restart failed builds. -==== Docusaurus +#### Docusaurus The documentation site is built with Docusaurus. Refer to the [README.md](doc/docusaurus/README.md) for more information. -==== Github Actions +#### Github Actions These perform some of the same checks as Hydra, but Github Actions is often more available, so they return faster and act as a "smoke check". -== Project roles and responsibilities - -- The regular contributors to the Haskell code, all of whom can review and merge PRs are: - - @ana-pantilie - - @bezirg - - @effectfully - - @kwxm - - @Unisay - - @zliu41 -- The maintainer of the Agda code is @jmchapman, @effectfully can help with small issues. -- If you have a technical dispute that you need help resolving, you can ask @zliu41. -- For problems with the developer environment setup, builds, or CI, you can ask @zeme-iohk or @zliu41. -- The regular contributors take turns releasing our software, but if you have a specific problem ask @zliu41. +## Project roles and responsibilities + +* The regular contributors to the Haskell code, all of whom can review and merge PRs are: +* @ana-pantilie +* @bezirg +* @effectfully +* @kwxm +* @Unisay +* @zliu41 +* The maintainer of the Agda code is @jmchapman, @effectfully can help with small issues. +* If you have a technical dispute that you need help resolving, you can ask @zliu41. +* For problems with the developer environment setup, builds, or CI, you can ask @zeme-iohk or @zliu41. +* The regular contributors take turns releasing our software, but if you have a specific problem ask @zliu41. diff --git a/DESCRIPTION.md b/DESCRIPTION.md index d8e5296cd93..38154ad7e1d 100644 --- a/DESCRIPTION.md +++ b/DESCRIPTION.md @@ -2,5 +2,5 @@ This repository hosts Plutus Core, the scripting language embedded in the Cardano ledger, which forms the basis of the Plutus Platform, an application development platform for developing distributed applications using the Cardano blockchain. -For more information please refer to the [README](./README.adoc). +For more information please refer to the [README](./README.md). diff --git a/README.adoc b/README.adoc deleted file mode 100644 index 0b4f68c0c14..00000000000 --- a/README.adoc +++ /dev/null @@ -1,73 +0,0 @@ -= https://github.com/IntersectMBO/plutus[Plutus Core] -:email: plutus@iohk.io -:author: Input Output HK Limited -:toc: left -:reproducible: - -== Introduction - -Plutus Core is the scripting language embedded in the Cardano ledger and forms the basis of the Plutus Platform, an application development platform for developing distributed applications using the Cardano blockchain. - -For more information about the projects, see the <>. - -This repository contains: - -* The implementation, specification, and mechanized metatheory of Plutus Core -* Plutus Tx, the compiler from Haskell to Plutus Core. - -For people who want to *use* the project, please consult the <>. - -== Development - -[[how-to-develop]] -=== How to develop and contribute to the project - -Run `nix develop` to enter the development shell and you will be presented with a list of available commands. - -**Please see link:CONTRIBUTING{outfilesuffix}[CONTRIBUTING] for comprehensive documentation on how to contribute to the project, including development and submitting changes* - -=== How to submit an issue - -Issues can be filed in the https://github.com/IntersectMBO/plutus/issues[GitHub Issue tracker]. - -=== How to depend on the project from another Haskell project - -The `plutus` libraries are published via https://input-output-hk.github.io/cardano-haskell-packages/[CHaP]. -See the information there for how to use CHaP. -After setting it up you should just be able to depend on the `plutus` packages as normal and cabal will find them. - -== Documentation - -=== User documentation - -The main documentation is located https://plutus.cardano.intersectmbo.org/docs/[here]. - -The haddock documentation is located https://plutus.cardano.intersectmbo.org/haddock/latest[here]. - -The documentation for the metatheory can be found https://plutus.cardano.intersectmbo.org/metatheory/latest[here]. - -=== Talks - -- https://www.youtube.com/watch?v=MpWeg6Fg0t8[Functional Smart Contracts on Cardano (2020)]: an overview of the ideas behind the Plutus Platform. -- https://www.youtube.com/watch?v=usMPt8KpBeI[The Plutus Platform (2020)]: an overview of the Platform as a whole (including the Application Framework) at the time. - -=== Specifications and design - -- https://plutus.cardano.intersectmbo.org/resources/plutus-report.pdf[Plutus Technical Report (draft)]: a technical report and design document for the project. -- https://plutus.cardano.intersectmbo.org/resources/plutus-core-spec.pdf[Plutus Core Specification]: the formal specification of the core language. -- https://plutus.cardano.intersectmbo.org/resources/extended-utxo-spec.pdf[Extended UTXO Model]: a design document for the core changes to the Cardano ledger. - -=== Academic papers - -- https://plutus.cardano.intersectmbo.org/resources/unraveling-recursion-paper.pdf[Unraveling Recursion]: a description of some of the compilation strategies used in Plutus IR (https://doi.org/10.1007/978-3-030-33636-3_15[published version]). -- https://plutus.cardano.intersectmbo.org/resources/system-f-in-agda-paper.pdf[System F in Agda]: a formal model of System F in Agda (https://doi.org/10.1007/978-3-030-33636-3_10[published version]). -- https://plutus.cardano.intersectmbo.org/resources/eutxo-paper.pdf[The Extended UTXO Model]: a full presentation of the EUTXO ledger extension (https://doi.org/10.1007/978-3-030-54455-3_37[published version]). -- https://plutus.cardano.intersectmbo.org/resources/utxoma-paper.pdf[UTXOma: UTXO with Multi-Asset Support]: a full presentation of the multi-asset ledger extension (https://doi.org/10.1007/978-3-030-61467-6_8[published version]). -- https://plutus.cardano.intersectmbo.org/resources/eutxoma-paper.pdf[Native Custom Tokens in the Extended UTXO Model]: a discussion of the interaction of the multi-asset support with EUTXO (https://doi.org/10.1007/978-3-030-61467-6_7[published version]). -- https://arxiv.org/abs/2201.04919[Translation Certification for Smart Contracts]: a certifier of Plutus IR compiler passes written in Coq. - -== Licensing - -You are free to copy, modify, and distribute this software under the terms of the Apache 2.0 license. - -See the link:./LICENSE.md[LICENSE] and link:./NOTICE.md[NOTICE] files for details. diff --git a/README.md b/README.md new file mode 100644 index 00000000000..29bbc2ec3ae --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# [Plutus Core](https://github.com/IntersectMBO/plutus) + +## Introduction + +Plutus Core is the scripting language embedded in the Cardano ledger and forms the basis of the Plutus Platform, an application development platform for developing distributed applications using the Cardano blockchain. + +For more information about the projects, see the [user-documentation](#user-documentation). + +This repository contains: + +* The implementation, specification, and mechanized metatheory of Plutus Core +* Plutus Tx, the compiler from Haskell to Plutus Core. + +For people who want to **use** the project, please consult the [user-documentation](#user-documentation). + +## Development + +### How to develop and contribute to the project + +Run `nix develop` to enter the development shell and you will be presented with a list of available commands. + +**Please see [CONTRIBUTING](CONTRIBUTING.md) for comprehensive documentation on how to contribute to the project, including development and submitting changes.** + +### How to submit an issue + +Issues can be filed in the [GitHub Issue tracker](https://github.com/IntersectMBO/plutus/issues). + +### How to depend on the project from another Haskell project + +The `plutus` libraries are published via [CHaP](https://input-output-hk.github.io/cardano-haskell-packages/). +See the information there for how to use CHaP. +After setting it up you should just be able to depend on the `plutus` packages as normal and cabal will find them. + +## Documentation + +### User documentation + +The main documentation is located [here](https://plutus.cardano.intersectmbo.org/docs/). + +The haddock documentation is located [here](https://plutus.cardano.intersectmbo.org/haddock/latest). + +The documentation for the metatheory can be found [here](https://plutus.cardano.intersectmbo.org/metatheory/latest). + +### Talks + +* [Functional Smart Contracts on Cardano (2020)](https://www.youtube.com/watch?v=MpWeg6Fg0t8): an overview of the ideas behind the Plutus Platform. +* [The Plutus Platform (2020)](https://www.youtube.com/watch?v=usMPt8KpBeI): an overview of the Platform as a whole (including the Application Framework) at the time. + +### Specifications and design + +* [Plutus Technical Report (draft)](https://plutus.cardano.intersectmbo.org/resources/plutus-report.pdf): a technical report and design document for the project. +* [Plutus Core Specification](https://plutus.cardano.intersectmbo.org/resources/plutus-core-spec.pdf): the formal specification of the core language. +* [Extended UTXO Model](https://plutus.cardano.intersectmbo.org/resources/extended-utxo-spec.pdf): a design document for the core changes to the Cardano ledger. + +### Academic papers + +* [Unraveling Recursion](https://plutus.cardano.intersectmbo.org/resources/unraveling-recursion-paper.pdf): a description of some of the compilation strategies used in Plutus IR ([published version](https://doi.org/10.1007/978-3-030-33636-3_15)). +* [System F in Agda](https://plutus.cardano.intersectmbo.org/resources/system-f-in-agda-paper.pdf): a formal model of System F in Agda ([published version](https://doi.org/10.1007/978-3-030-33636-3_10)). +* [The Extended UTXO Model](https://plutus.cardano.intersectmbo.org/resources/eutxo-paper.pdf): a full presentation of the EUTXO ledger extension ([published version](https://doi.org/10.1007/978-3-030-54455-3_37)). +* [UTXOma: UTXO with Multi-Asset Support](https://plutus.cardano.intersectmbo.org/resources/utxoma-paper.pdf): a full presentation of the multi-asset ledger extension ([published version](https://doi.org/10.1007/978-3-030-61467-6_8)). +* [Native Custom Tokens in the Extended UTXO Model](https://plutus.cardano.intersectmbo.org/resources/eutxoma-paper.pdf): a discussion of the interaction of the multi-asset support with EUTXO ([published version](https://doi.org/10.1007/978-3-030-61467-6_7)). +* [Translation Certification for Smart Contracts](https://arxiv.org/abs/2201.04919): a certifier of Plutus IR compiler passes written in Coq. + +## Licensing + +You are free to copy, modify, and distribute this software under the terms of the Apache 2.0 license. + +See the [LICENSE](./LICENSE.md) and [NOTICE](./NOTICE.md) files for details. diff --git a/RELEASE.adoc b/RELEASE.md similarity index 58% rename from RELEASE.adoc rename to RELEASE.md index 09a0337f238..f57308ad3b8 100644 --- a/RELEASE.adoc +++ b/RELEASE.md @@ -1,37 +1,34 @@ -= Plutus Release Process -:toc: left -:reproducible: -:figure-caption!: +# Plutus Release Process -== Packages +## Packages The following packages are versioned and released: -- `plutus-core`, which provides -* The main library `plutus-core:plutus-core` -* Three executables, `plutus-core:pir`, `plutus-core:plc`, `plutus-core:uplc` -- `plutus-ledger-api` -* The main library `plutus-ledger-api:plutus-ledger-api` -- `plutus-tx` -* The main library `plutus-tx:plutus-tx` -- `plutus-tx-plugin` -* The main library `plutus-tx-plugin:plutus-tx-plugin` +* `plutus-core`, which provides + * The main library `plutus-core:plutus-core` + * Three executables, `plutus-core:pir`, `plutus-core:plc`, `plutus-core:uplc` +* `plutus-ledger-api` + * The main library `plutus-ledger-api:plutus-ledger-api` +* `plutus-tx` + * The main library `plutus-tx:plutus-tx` +* `plutus-tx-plugin` + * The main library `plutus-tx-plugin:plutus-tx-plugin` -== Version Scheme +## Version Scheme All above packages are versioned and released in sync. That is, whenever one package releases version X, so do all other packages. -We follow https://pvp.haskell.org/[the PVP version scheme] for the packages, where version numbers have a `major.major.minor.patch` pattern with the following semantics: +We follow [the PVP version scheme](https://pvp.haskell.org/) for the packages, where version numbers have a `major.major.minor.patch` pattern with the following semantics: -- A major release (e.g., 3.5.2.1 -> 3.6.0.0 or 4.0.0.0) may contain arbitrary changes to the API (subject to the backwards compatibility requirements described below). -- A minor release (e.g., 3.5.2.1 -> 3.5.3.0) may contain changes such as new features, which technically can be breaking, but are unlikely to cause breakage in practice, and if it does, the fix should be straightforward (e.g., by renaming). -+ -A minor release is also allowed to change the observable behaviors of functions that users should not rely on. -For example, if a function returns a list of things and makes no promise on the order of elements in the returned list, then a minor release may change the order. -- A patch release (e.g., 3.5.2.1 -> 3.5.2.2) can only contain such things as simple bug fixes, performance improvements, and documentation updates. +* A major release (e.g., 3.5.2.1 -> 3.6.0.0 or 4.0.0.0) may contain arbitrary changes to the API (subject to the backwards compatibility requirements described below). +* A minor release (e.g., 3.5.2.1 -> 3.5.3.0) may contain changes such as new features, which technically can be breaking, but are unlikely to cause breakage in practice, and if it does, the fix should be straightforward (e.g., by renaming). -== Release Frequency + A minor release is also allowed to change the observable behaviors of functions that users should not rely on. + For example, if a function returns a list of things and makes no promise on the order of elements in the returned list, then a minor release may change the order. +* A patch release (e.g., 3.5.2.1 -> 3.5.2.2) can only contain such things as simple bug fixes, performance improvements, and documentation updates. + +## Release Frequency We will begin by making a new major or minor release every 4 weeks, and evaluate and adjust the frequency in the future. Ad-hoc releases can be made upon request. @@ -39,17 +36,17 @@ Ad-hoc releases can be made upon request. We only make patch releases for version `x.y.z`, if a cardano-node release candidate depends on `x.y.z.w`, and an issue is found during the node release process that requires patching plutus. In all other cases, we always start a new major or minor release from master. -== Release Process +## Release Process Run `./scripts/interactive-release.sh` to manage the release process. -== Make Broken Release Unbuildable +## Make Broken Release Unbuildable Once a release has been published to CHaP, it cannot be removed. If a release is found to be broken or unsafe, it should be marked as unbuildable in CHaP to prevent cabal from selecting it during dependency resolution. This can be accomplished by setting `base < 0` in the `build-depends` section of each affected `.cabal` file. -To do this, follow https://github.com/IntersectMBO/cardano-haskell-packages?tab=readme-ov-file#how-to-add-a-new-package-metadata-revision[the instructions] provided in the CHaP documentation. In summary: clone the CHaP repository, download the https://github.com/intersectmbo/cardano-haskell-packages/archive/refs/heads/repo.zip[pre-built repository] into the `_repo` top-level directory, run `./scripts/add-revision.sh _repo PACKAGE_NAME PACKAGE_VERSION` for each relevant `plutus-*` package and for the target version (e.g. `./scripts/add-revision _repo plutus-core 1.54.0.0`) then commit and push your changes to CHaP. Please refer to this https://github.com/IntersectMBO/cardano-haskell-packages/pull/1178[pull request] as an example. +To do this, follow [the instructions](https://github.com/IntersectMBO/cardano-haskell-packages?tab=readme-ov-file#how-to-add-a-new-package-metadata-revision) provided in the CHaP documentation. In summary: clone the CHaP repository, download the [pre-built repository](https://github.com/intersectmbo/cardano-haskell-packages/archive/refs/heads/repo.zip) into the `_repo` top-level directory, run `./scripts/add-revision.sh _repo PACKAGE_NAME PACKAGE_VERSION` for each relevant `plutus-*` package and for the target version (e.g. `./scripts/add-revision _repo plutus-core 1.54.0.0`) then commit and push your changes to CHaP. Please refer to this [pull request](https://github.com/IntersectMBO/cardano-haskell-packages/pull/1178) as an example. -== Backwards Compatibility with Cardano API +## Backwards Compatibility with Cardano API It is a good idea to avoid breaking the latest version of Cardano API in a new Plutus release. This makes it easy for downstream projects to update Plutus version without needing a new Cardano API release. @@ -57,14 +54,14 @@ This makes it easy for downstream projects to update Plutus version without need For example, suppose we make some improvements to `plutus-tx-plugin` and make a new major release. Since all packages are released in sync, we also make a new major release for `plutus-core`. Although it is a major release, we should avoid making changes that is incompatible with the latest version of Cardano API. -Otherwise, downstream projects such as Plutus Tools won't be able to use the new Plutus version and take advantage of the plugin improvements, until a new Cardano API version is published. +Otherwise, downstream projects such as Plutus Tools won’t be able to use the new Plutus version and take advantage of the plugin improvements, until a new Cardano API version is published. To do so, rather than making changes to the Plutus API that breaks Cardano API (e.g., changing the type of a function), we can temporarily keep both the old Plutus API and the new Plutus API, until a new Cardano API version is released that no longer depends on the old Plutus API. This is not a hard rule, and does not need to be strictly adhered to if it is too much trouble for small or unclear benefits. This will not be needed once Cardano API starts to make more frequent releases. -== Participation in the `cardano-node` release process +## Participation in the `cardano-node` release process Some Plutus features and changes require integration testing on devnets and testnets. Such tests are typically performed by the node QA team or the ecosystem collaborators. diff --git a/STYLEGUIDE.adoc b/STYLEGUIDE.md similarity index 67% rename from STYLEGUIDE.adoc rename to STYLEGUIDE.md index 5392501450b..c5a25db92ec 100644 --- a/STYLEGUIDE.adoc +++ b/STYLEGUIDE.md @@ -1,5 +1,4 @@ -= Plutus Haskell style guide -:toc: +# Plutus Haskell style guide The purpose of this document is to codify various aspects of how we write Haskell code as we contribute to Plutus. @@ -10,33 +9,30 @@ The goal is twofold: The contents of this document are “recommendations”, that is: -- They are more concrete than “principles” (like “write simple code”) because we want them to be clearly action-guiding. +* They are more concrete than “principles” (like “write simple code”) because we want them to be clearly action-guiding. +* They are more vague than “rules” (like “never use function X”) because it’s usually impossible to be that precise, and so judgement is still required in applying them. -- They are more vague than “rules” (like “never use function X”) because it’s usually impossible to be that precise, and so judgement is still required in applying them. +## Haskell -== Haskell - -=== Basic formatting +### Basic formatting Use 2 spaces for indentation. Full-indent for `where` is recommended. Bad: -[source,Haskell] ----- +```Haskell f x = y where y = ... ----- +``` Good: -[source,Haskell] ----- +```Haskell f x = y where y = ... ----- +``` Avoid lines over 100 characters. This is codified in `.editorconfig`. @@ -48,32 +44,28 @@ Type signatures should have the double-colon on the same line as the name, separ Bad: -[source,Haskell] ----- +```Haskell foo :: A -> B -> C ----- +``` Good: -[source,Haskell] ----- +```Haskell foo :: A -> B -> C ----- +``` Good: -[source,Haskell] ----- +```Haskell foo :: A -> B -> C ----- +``` Good: -[source,Haskell] ----- +```Haskell foo :: forall a b c. ( C1 a @@ -82,20 +74,20 @@ foo :: A -> B -> C ----- +``` -==== Reasoning +#### Reasoning The indentation is arbitrary. The line length is longer than the “traditional” 80 characters, but anecdotally Haskell code tends toward longer lines, and breaking them up doesn’t help readability much. Placing the double-colon on the same line makes it easier to find definitions. -We don't necessarily have HLS running all the time, and it may not work on every module. +We don’t necessarily have HLS running all the time, and it may not work on every module. `stylish-haskell` is a fairly modest formatter that doesn’t touch too much. At the moment we mostly use it for formatting imports! -In the past we tried `ormolu`, but its https://github.com/tweag/ormolu/issues/641[lack of support for multi-line comments] was a deal-breaker. +In the past we tried `ormolu`, but its [lack of support for multi-line comments](https://github.com/tweag/ormolu/issues/641) was a deal-breaker. -=== Write type signatures +### Write type signatures Every top-level binding should have a type signature. _Most_ other named bindings (e.g. `let` and `where` bindings) should also have type signatures. @@ -103,86 +95,81 @@ Constraints should be uncurried. Bad: -[source,Haskell] ----- +```Haskell foo :: Eq a => Ord a => a -> a -> a ----- +``` Good: -[source,Haskell] ----- +```Haskell foo :: (Eq a, Ord a) => a -> a -> a ----- +``` -==== Reasoning +#### Reasoning -Haskell is traditionally lauded for its excellent type inference. People used to make the argument that this brought Haskell closer to dynamically-typed languages in ease of use: you don't have to write type signatures, the compiler will infer it for you. +Haskell is traditionally lauded for its excellent type inference. People used to make the argument that this brought Haskell closer to dynamically-typed languages in ease of use: you don’t have to write type signatures, the compiler will infer it for you. However, not writing type signatures has a heavy _maintainability_ cost. -===== Non-local errors +##### Non-local errors If types are inferred, then the way a binding is _used_ can affect the type which is inferred for it. That means that a mistake in using a binding can result in an error _inside_ the binding (or elsewhere) due to the inferred type not matching what goes on in the RHS of the binding. More generally, errors can end up appearing in unexpected and counter-intuitive places. Pinning down the type of a binding means that any errors relating to using that binding will occur at the use site, where they belong! -===== Documentation +##### Documentation -It's a Haskell truism that the type forms part of the documentation of a binding. But that requires you to be able to _see_ the type. Of course, in this day and age, we should all have an IDE that shows us the type on hover. But sometimes you're stuck using `vim`. Or you're reviewing the code on Github. Or the IDE is broken. +It’s a Haskell truism that the type forms part of the documentation of a binding. But that requires you to be able to _see_ the type. Of course, in this day and age, we should all have an IDE that shows us the type on hover. But sometimes you’re stuck using `vim`. Or you’re reviewing the code on Github. Or the IDE is broken. So do your colleagues a favor and just write it down. -===== Silent changes +##### Silent changes Changing the type of a binding often means that something relatively significant has changed. But if the type is inferred, this can happen without you noticing it. This is almost always bad! -=== Write kind signatures +### Write kind signatures Every type definition that has parameters which are not all of kind `Type` should have a kind signature using `StandaloneKindSignatures`. -==== Examples +#### Examples Bad: -[source,Haskell] ----- +```Haskell data Term tyname name uni fun a ----- +``` Good: -[source,Haskell] ----- +```Haskell type Term :: Type -> Type -> (Type -> Type) -> Type -> Type -> Type data Term tyname name uni fun a ----- +``` -==== Reasoning +#### Reasoning -The reasoning is essentially the same as for type signatures. We've got used to GHC inferring all this, and in the past we didn't even have the means to easily state kind signatures. But these days with people using fancier type-level machinery, and with better support for kind signatures, it's time to just start writing them down. +The reasoning is essentially the same as for type signatures. We’ve got used to GHC inferring all this, and in the past we didn’t even have the means to easily state kind signatures. But these days with people using fancier type-level machinery, and with better support for kind signatures, it’s time to just start writing them down. -=== Write Haddock +### Write Haddock Every top-level exported binding should have Haddock. Non-exported bindings should probably have Haddock too. Put the module’s haddock comment _right_ above the `module M where` line, and below the PRAGMAs. -==== Reasoning +#### Reasoning -https://www.michaelpj.com/blog/2022/04/24/on-commenting-code.html[Comment your code!] +[Comment your code!](https://www.michaelpj.com/blog/2022/04/24/on-commenting-code.html) -=== Prefer pattern-matching +### Prefer pattern-matching Prefer to use pattern matching where possible, unless it significantly complicates the code. -==== Examples +#### Examples -===== Instead of an equality check +##### Instead of an equality check Bad: -[source,Haskell] ----- +```Haskell data SortOrder = Ascending | Descending deriving Eq @@ -190,240 +177,228 @@ sortWithOrder' :: Ord a => SortOrder -> [a] -> [a] sortWithOrder' order = f . sort where f = if order == Ascending then id else reverse ----- +``` Good: -[source,Haskell] ----- +```Haskell sortWithOrder :: Ord a => SortOrder -> [a] -> [a] sortWithOrder Ascending = id . sort sortWithOrder Descending = reverse . sort ----- +``` -===== Instead of destructor functions +##### Instead of destructor functions Bad: -[source,Haskell] ----- +```Haskell either f g x ----- +``` Good: -[source,Haskell] ----- +```Haskell case x of Left e -> f e Right s -> g s ----- +``` But this might be okay: -[source,Haskell] ----- +```Haskell fmap (either f g) eithers ----- +``` -==== Reasoning +#### Reasoning Pattern matching is easy to read, and allows the compiler to give better errors and warnings (e.g. incomplete match warnings). -=== Avoid punning +### Avoid punning Avoid using the same names for things at the term and type level. Except for `newtype`` constructors. -==== Examples +#### Examples Bad: -[source,Haskell] ----- +```Haskell data Foo = Foo Int ----- +``` Good: -[source,Haskell] ----- +```Haskell data Foo = MkFoo Int ----- +``` -=== Avoid ticks in function names +### Avoid ticks in function names Generally avoid using ticks to distinguish function names. All this conveys is that it is “another” version of the function. Try expressing the difference in the function name, even if it makes it longer. -==== Examples +#### Examples Bad: -[source,Haskell] ----- +```Haskell runCek runCek’ ----- +``` Good: -[source,Haskell] ----- +```Haskell runCek runCekWithLogs ----- +``` -==== Reasoning +#### Reasoning It’s a tempting naming convention, but no one likes reading code with such functions. The function names should convey helpful information when possible. -=== Avoid multi-line parenthesized expressions +### Avoid multi-line parenthesized expressions A parenthesized expression should not span multiple lines. Pull it out to a named binding, use `$`, or otherwise reorganize the code. -==== Examples +#### Examples Bad: -[source,Haskell] ----- +```Haskell foldr (\a acc -> let x = ... in a + x + acc) x xs ----- +``` Good: -[source,Haskell] ----- +```Haskell foldr meaningfulName x xs where meaningfulName :: ... meaningfulName a acc = let x = ... in a + x + acc ----- +``` -==== Reasoning +#### Reasoning A parenthesis forces the user to keep a stack in their head to remember when the current "argument" finishes. Line length limits this to some degree, but if we allow line breaks then the amount of stack can become quite unwieldy. -This also explains why `$` is good: since it indicates there will be no closing paren, there is no need for a stack (it's the "tail call" of bracketing). +This also explains why `$` is good: since it indicates there will be no closing paren, there is no need for a stack (it’s the "tail call" of bracketing). -=== Use deriving strategies +### Use deriving strategies Always use deriving strategies. -==== Reasoning +#### Reasoning Not using deriving strategies requires the compiler to guess which strategy you want. This can have consequences, especially when `DeriveAnyClass` is enabled, since you can accidentally end up using anyclass deriving when you didn’t mean to. Better to be explicit. -=== Use Text +### Use Text Use Text instead of String unless you have a good reason not to. -==== Reasoning +#### Reasoning It’s 2022, use a proper, unicode aware string type instead of a linked list. -=== Derive `Show`, define `Pretty`, use `Pretty` for human-readable output +### Derive `Show`, define `Pretty`, use `Pretty` for human-readable output Always derive `Show`, do not define it manually. Always use `Pretty` for human-readable output, not `Show`. -Always define `Pretty` explicitly (when you need it). It's okay to delegate to the `Show` instance if you think it's good enough. +Always define `Pretty` explicitly (when you need it). It’s okay to delegate to the `Show` instance if you think it’s good enough. -==== Reasoning +#### Reasoning The derived version of `Show` is always useful as a way of seeing the explicit structure of a value as a Haskell value. -Defining `Show` can mean that this is no longer true, and you can't do a better job than the derived version. +Defining `Show` can mean that this is no longer true, and you can’t do a better job than the derived version. -We use `Pretty` always for human-readable output, because it's actually friendly to layout and the derived `Show` instance is not usually human friendly. +We use `Pretty` always for human-readable output, because it’s actually friendly to layout and the derived `Show` instance is not usually human friendly. Therefore if you need to produce output for humans, define a `Pretty` instance. This will typically need to be hand-written, unless it happens that you can defer to the `Show` instance, e.g. for simple enums `Show` can be fine since it just prints the constructor names. -=== Use the package name as the root of the module name hierarchy +### Use the package name as the root of the module name hierarchy If the package is `foo-bar`, then the modules should all be `FooBar.X`. -==== Reasoning +#### Reasoning -See “Naming conventions” https://www.haskellforall.com/2021/05/module-organization-guidelines-for.html[here]. We do it slightly differently (“FooBar” rather than “Foo.Bar”), but the main principle is the same. +See “Naming conventions” [here](https://www.haskellforall.com/2021/05/module-organization-guidelines-for.html). We do it slightly differently (“FooBar” rather than “Foo.Bar”), but the main principle is the same. -=== Reading direction +### Reading direction -Try to keep a single line *mostly* reading left-to-right or right-to-left. +Try to keep a single line **mostly** reading left-to-right or right-to-left. -==== Examples +#### Examples Bad: -[source,Haskell] ----- +```Haskell traverse (\x -> … ----- +``` -==== Reasoning +#### Reasoning -Haskell can get quite condensed and hard to read, especially when the reading direction changes frequently. Often there are symmetrical versions of operators like `<=<` and `>=>` or `=<<` and `>>=` that you can switch between to make code easier to read. +Haskell can get quite condensed and hard to read, especially when the reading direction changes frequently. Often there are symmetrical versions of operators like `<=<` and `>=>` or `=xref:` and `[]=` that you can switch between to make code easier to read. -=== Avoid orphan instances except where they’re safe +### Avoid orphan instances except where they’re safe -Avoid orphan instances, but don’t worry about it if https://www.michaelpj.com/blog/2020/10/29/your-orphans-are-fine.html[you can be sure that they’re safe]. +Avoid orphan instances, but don’t worry about it if [you can be sure that they’re safe](https://www.michaelpj.com/blog/2020/10/29/your-orphans-are-fine.html). -==== Reasoning +#### Reasoning See the blog post. -=== Make data strict by default, use `nothunks` for long-lived data structures +### Make data strict by default, use `nothunks` for long-lived data structures Use `StrictData` for new code; make fields strict unless you have a good reason not to. -For data structures that might live for a long time, use `nothunks` to assert that they don't contain unexpected thunks. +For data structures that might live for a long time, use `nothunks` to assert that they don’t contain unexpected thunks. -==== Reasoning +#### Reasoning A painful lesson of Haskell in production is that space leaks really suck, are a huge pain to track down, and can originate in surprising locations. -This suggests that it's worth a bit of proactive paranoia: just make things strict as much as possible, in the hopes of squashing any nascent space leaks. +This suggests that it’s worth a bit of proactive paranoia: just make things strict as much as possible, in the hopes of squashing any nascent space leaks. -This may seem like overkill to you... until you've experienced debugging a space leak! +This may seem like overkill to you... until you’ve experienced debugging a space leak! -=== Extensions +### Extensions -==== The Good +#### The Good These are basically all fine and can be put in `default-extensions`. -- Anything in https://github.com/ghc-proposals/ghc-proposals/pull/380[`GHC2021`]. Once we have a GHC version that supports the GHC2021 language, we will likely switch to using it. -- `LambdaCase`: clear, helpful -- `DerivingStrategies`: always -- `GADTs`: well established, useful -- `OverloadedStrings`: essential when working with `Text`, which you should -- `NegativeLiterals` -- `DerivingVia`: great -- `RoleAnnotations`: if you need it, you need it +* Anything in [`GHC2021`](https://github.com/ghc-proposals/ghc-proposals/pull/380). Once we have a GHC version that supports the GHC2021 language, we will likely switch to using it. +* `LambdaCase`: clear, helpful +* `DerivingStrategies`: always +* `GADTs`: well established, useful +* `OverloadedStrings`: essential when working with `Text`, which you should +* `NegativeLiterals` +* `DerivingVia`: great +* `RoleAnnotations`: if you need it, you need it -==== The Situational +#### The Situational -The following extensions are generally fine if you find that they’ll make your life much easier, but you probably don’t want to use them *all* the time. +The following extensions are generally fine if you find that they’ll make your life much easier, but you probably don’t want to use them **all** the time. -- `RecordWildCards` -- `TypeFamilies`: often very useful, but can make things tricky. Think before using. -- `DataKinds` -- `FunctionalDependencies` -- `ViewPatterns`: can be very nice, can be a huge mess -- `OverloadedLists`: sometimes a lifesaver, not as indispensable as `OverloadedStrings` +* `RecordWildCards` +* `TypeFamilies`: often very useful, but can make things tricky. Think before using. +* `DataKinds` +* `FunctionalDependencies` +* `ViewPatterns`: can be very nice, can be a huge mess +* `OverloadedLists`: sometimes a lifesaver, not as indispensable as `OverloadedStrings` -==== The Bad +#### The Bad `UnicodeSyntax`: not worth it -== Imports +## Imports -=== Avoid complex imports statements +### Avoid complex imports statements If you find you have: 1. A long explicit import list @@ -431,98 +406,94 @@ If you find you have: Then either just import the module in its entirety, or qualify it. Usually if you are using hiding you will need to qualify it. -==== Examples +#### Examples Bad: -[source,Haskell] ----- +```Haskell import Control.Lens (first, … , _Right) hiding (ix, lens) ----- +``` Good: -[source,Haskell] ----- +```Haskell import Control.Lens qualified as Lens ----- +``` -==== Reasoning +#### Reasoning Complex import statements are difficult to maintain and cause annoying diffs which are also hard to merge. Qualified function usages are quite easy to read, and not that much worse to write. -=== Prefer importing specific module to the whole umbrella +### Prefer importing specific module to the whole umbrella When working inside a package that exports an “umbrella module”, avoid importing that module directly. -==== Examples +#### Examples Bad: -[source,Haskell] ----- +```Haskell import PlutusCore ----- +``` Good: -[source,Haskell] ----- +```Haskell import PlutusCore.Name.Unique ----- +``` -==== Reasoning +#### Reasoning Since the umbrella module likely imports everything else, it is easy to accidentally end up with cyclic imports if you import it. Outside the package where it is defined this is usually not a problem. -== Libraries +## Libraries -=== Prefer using the strict versions of data structures +### Prefer using the strict versions of data structures Use the strict versions of most data structures by default unless you have a good reason not to. -==== Reasoning +#### Reasoning Lazy data structures are easy ways to get space leaks, and the performance difference is typically negligible. -=== Use lenses judiciously +### Use lenses judiciously Use lenses where they allow a significant simplification of the code. For simple use cases just use normal record accessors. -==== Reasoning +#### Reasoning Arguably if we’re going to allow lenses in our codebase and force people to know about them, we should commit to them wholesale and use them everywhere. But in practice we just use them for places where they’re hard to beat. -=== data-default +### data-default Don’t use `data-default`, instead just define `defaultX` values for your `X` type. -==== Reasoning +#### Reasoning `Default` is not terribly bad. It’s truly ad-hoc polymorphism: all you get is name reuse, you can’t (or shouldn’t) write a function that’s polymorphic over `Default a`. That’s fine, but it also means that the benefit is fairly marginal. Additionally, just defining specific values is more flexible. If, say, you want multiple default values for different contexts, then that is straightforward with values but not with `Default`. -== Cabal +## Cabal -=== default-language +### default-language Use Haskel2010. -==== Reasoning +#### Reasoning It’s the latest. -=== default-extensions +### default-extensions Put your commonly-used extensions in `default-extensions` rather than repeating them constantly. -==== Reasoning +#### Reasoning It’s nice for files to be self-contained, but this is typically a fiction: you need to know about compilation flags from cabal files anyway. It saves a lot of typing to put the really essential stuff in the cabal file. Haskell “languages” are basically a blessed set of extensions anyway, and people are fine putting those in the cabal file. A lot of what we’re currently doing is manually implementing the GHC2021 language! -=== Warning flags +### Warning flags Use the following set of warning flags: @@ -537,42 +508,42 @@ Use the following set of warning flags: -Wmissing-deriving-strategies ``` -==== Reasoning +#### Reasoning GHC’s warnings are generally pretty good. `-Wall` doesn’t include them all, so we add some additional useful ones. -=== Warnings as errors +### Warnings as errors Don’t set `-Werror` by default, only set it in CI builds. -==== Reasoning +#### Reasoning Working with `-Werror` enabled is very disruptive, because you can’t e.g. have an unused variable or import even temporarily. However, it is very useful to keep our code warning-free, so setting `-Werror` in CI is recommended. -=== Common stanzas +### Common stanzas Use a common stanza (usually called “lang”) to include a) the language (Haskell2010), b) the `default-extensions`, c) the default set of warnings. -==== Reasoning +#### Reasoning Common stanzas are great and make it easier to keep things in sync. -=== Multiple public libraries +### Multiple public libraries Use multiple public libraries judiciously. For now, only use them for additional libraries to be used in test code (“testlibs”). -==== Reasoning +#### Reasoning Multiple public libraries are a very useful feature, but they’re not entirely mature yet. One place where they are invaluable is to export a “test library” containing code for testing the main library, without forcing the main library to depend on test libraries. In due course we may want to use them more widely. -=== Internal libraries +### Internal libraries Use internal libraries where it is useful to enforce a clear separation of a “sub-package”. -==== Reasoning +#### Reasoning Internal libraries are fairly well supported and make it easy to totally segregate a “sub-package” from the main library. This can be useful for, say, a standalone implementation of a data structure, or similar. diff --git a/plutus-benchmark/marlowe/README.md b/plutus-benchmark/marlowe/README.md index 56cb0e7dca0..4e8b59c0686 100644 --- a/plutus-benchmark/marlowe/README.md +++ b/plutus-benchmark/marlowe/README.md @@ -28,7 +28,7 @@ The benchmarking portion of the code lives in `marlowe/bench`, which depends on (2) Profiling: look at each script in more detail, what functions are taking up the most budget? How can they be optimized? -See [CONTRIBUTING.md](https://github.com/IntersectMBO/plutus/blob/master/CONTRIBUTING.adoc#how-to-build-the-code-with-profiling) for profiling instructions. +See [CONTRIBUTING.md](https://github.com/IntersectMBO/plutus/blob/master/CONTRIBUTING.md#how-to-build-the-code-with-profiling) for profiling instructions. Of the most common Marlowe transactions, input application transactions are the most relevant, as they are complex and can go over the execution limits at times. So there is a priority on examining those contracts. diff --git a/scripts/check-broken-links.sh b/scripts/check-broken-links.sh index ee1c520fee8..bc471fdc047 100755 --- a/scripts/check-broken-links.sh +++ b/scripts/check-broken-links.sh @@ -2,7 +2,6 @@ TARGETS=( .github/{ISSUE_TEMPLATE/*,*.md,*.yml} **/{LICENSE,NOTICE,README.md,TRIAGE.md} CODE_OF_CONDUCT.md - *.adoc ) # For some reason linkchecker fails to check these URLs though they are valid.