A well formatted question deserves a well formatted answer! I will go into some depths, but also keep some things at a “magical” level. This is a long read so take your time!
Before we start, it is good to note that most of the Cardano stack is written in Haskell, and thus, managing a Plutus project is just managing a Haskell project. There are multiple ways of doing this, just a plain Cabal project or one of the many Nix ways of building (cabal2nix, Haskell.nix and many others). Now there are good reason to use these Nixs ways (most notably reproducibility and caching), but in this answer I will focus on a more understandable way of just using GHC + cabal on Ubuntu 22.04 (not the minimal install).
A disclaimer, building everything yourself requires lots of ram (you can also re-run the cabal run
command if you run out of memory mid-process). The good thing, however, once build you have these Haskell artifact locally prebuilt in your ~/.cabal/
folder.
The dependencies
Before we can even build a general all-purpose Haskell/plutus project, we need some basic building tools necessary to even start compiling! These mainly consist of the following tools,
- OS dependent dependencies
- GHC and Cabal
- A special version of libsodium
- A special version of libsecp256k1
OS dependent dependencies
To build everything, you at least need these dependencies
sudo apt-get update -y && sudo apt-get upgrade -y
sudo apt-get install automake build-essential pkg-config libffi-dev libgmp-dev libssl-dev libtinfo-dev libsystemd-dev zlib1g-dev make g++ tmux git jq wget libncursesw5 curl libtool postgresql libpq-dev autoconf -y
Note that translating these OS dependent dependencies to other operating systems is hard and out of the scope of this post (this is why you might want nix). They may also change in the future as the project might add new dependencies.
GHC and Cabal
It is recommended by IOG to use GHC version 8.10.7 and Cabal version 3.6.2.0. These can be installed via GHCUP via,
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
You can press enter for all question the installer asks. Then reload your terminal (to make ghcup
appear in your path) and,
ghcup install ghc 8.10.7
ghcup set ghc 8.10.7
ghcup install cabal 3.6.2.0
ghcup set cabal 3.6.2.0
Reload your terminal again to make these appear in your path and set
cabal configure --with-compiler=ghc-8.10.7
A special version of libsodium
Libsodium is a tool chain (written for performance) for cryptographic derivatives, for example you can create signatures and hashes with it. We require a particular version of it that adds VRF cryptography to it (used for the Cardano consensus mechanism). Installing this can be done via,
git clone https://github.com/input-output-hk/libsodium
cd libsodium
git checkout 66f017f1
./autogen.sh
./configure
make
sudo make install
In addition to this add the following to your ~/.bashrc
and reload your terminal again to add it to your path
export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
A special version of libsecp256k1
Since the Vasil hard fork Cardano has the secp256K1 signature scheme available in plutus. This allows for the validation of Bitcoin signatures and will make it easier to build bridges. Bitcoin is not the only blockchain that use this signature scheme, many others do. So to install this library (again a particular audited version) you use,
git clone https://github.com/bitcoin-core/secp256k1
cd secp256k1
git checkout ac83be33
./autogen.sh
./configure --enable-module-schnorrsig --enable-experimental
make
make check
sudo make install
A preconfigure project
We are ready to build a Plutus project! Before I tell how you can set up such a project, I want to share an already set up example. This so that you can test that you correctly installed the dependencies above. To run this example you use
git clone https://github.com/perturbing/example-project-basic.git
cd example-project-basic
cabal update
cabal run
I would like to reiterate, if you do not have sufficient ram available, for example because you run this example in a VM, you can cabal run
again if your memory overflows. This example ran for me in a vm with 10gb ram.
Now how is this project setup?
This project and its structure is not constructed arbitrarily. It is namely derived from the latest release of the plutus-apps repository. This repository and its management of dependencies is chosen because it imports and exposes almost all important Haskell packages written for Cardano.
Now, how to concretely set up and copy the essential bits of this repo? It consists of the following steps.
To start we execute
mkdir my-plutus-project
cd my-plutus-project
cabal init
touch cabal.project
You could also use the flag --interactive
for a more detailed set up with cabal init
.
Next, we copy the cabal.project
file of plutus-apps from its latest release (do not pick the main branch, consider this is unstable). Currently, the latest version of this file is this. Next, we need to adjust some pieces of this file to make it work for us.
The first thing to change, this cabal.project
file does not import the plutus-apps
repo (since it was part of the original one). So, we need to add that by changing the code that declared the to build packages of the plutus-apps repo. It also does not declare our new build artifacts of our cabal project, we have to change the exposed packages it has to build. Both can be achieved by changing,
packages: cardano-streaming
doc
freer-extras
marconi
marconi-mamba
playground-common
pab-blockfrost
plutus-chain-index
plutus-chain-index-core
plutus-contract
plutus-contract-certification
plutus-example
plutus-ledger
plutus-ledger-constraints
plutus-pab
plutus-pab-executables
plutus-script-utils
plutus-tx-constraints
plutus-use-cases
rewindable-index
to
packages: ./
source-repository-package
type: git
location: https://github.com/input-output-hk/plutus-apps
tag: the_latest_release_tag_used_here_CHANGE_ME!!!
subdir:
cardano-streaming
doc
freer-extras
marconi
marconi-mamba
playground-common
pab-blockfrost
plutus-chain-index
plutus-chain-index-core
plutus-contract
plutus-contract-certification
plutus-example
plutus-ledger
plutus-ledger-constraints
plutus-pab
plutus-pab-executables
plutus-script-utils
plutus-tx-constraints
plutus-use-cases
rewindable-index
Next up, we can add to our executables and/or libraries on which modules they depend. For example, we might add to our my-plutus-project.cabal
file that the executable depends on
build-depends: base ^>=4.14.3.0
, aeson
, bytestring
, containers
, cardano-api
, data-default
, plutus-ledger
, plutus-ledger-api
, plutus-ledger-constraints
, plutus-script-utils
, plutus-tx-plugin
, plutus-tx
, text
, serialise
And lastly, we can write our app/Main.hs
file to execute some functions for us. A simple cabal update
and a cabal run
will execute your Main.hs
executable.
Some last words
This is a long answer, and in no way is complete. I advise anyone who is eager to learn more about Haskell to take the Haskell course from IOG (it also covers cabal!). I hope this gets you started!