crypto.bi – ELI5 Cryptography, cryptocurrency and programming

Building the Haskell cardano-node on Linux

tl;dr;

Jump straight to the build instructions

The Details

Looking forward to the Haskell mainnet, the next step in the Cardano project, I’ve begun exploring the cardano-node Haskell implementation.

I find that exploring source code is much easier if you actually watch the program run for a while. So, the first thing a developer has to do is build the project!

So, while trying to build cardano-node using the published instructions, I ran into a few issues. In this post I’ll be sharing my experience, which hopefully will be useful to others.

I’m not a Haskell expert. I’ve worked with functional programming before, but mainly in hybrid languages such as Ocaml and Scala. Haskell is 100% purely functional, so it requires a different approach to several problems. In Scala, for instance, you can fall back to Java idioms if you need to in order to solve some problems. In Haskell you can’t. It’s 100% functional.

So, if you’re an experienced Haskell developer, some of the steps taken here may sound redundant or non idiomatic. I’m sorry about that.

So let’s jump right into the hackage.

Building cardano-node

According to the Github project’s README, all I gotta do is:

$ cd cardano-node
$ cabal build

So, let’s try it.

$ cabal build
Warning: The build command is a part of the legacy v1 style of cabal usage.

Please switch to using either the new project style and the new-build command
or the legacy v1-build alias as new-style projects will become the default in
the next version of cabal-install. Please file a bug if you cannot replicate a
working v1- use case with the new-style commands.

For more information, see: https://wiki.haskell.org/Cabal/NewBuild

cabal: No cabal file found.
Please create a package description file <pkgname>.cabal

Alright, that didn’t fly. So I need a cabal file. Only thing that looks like a cabal file in here is cabal.project, but apparently I need the extension to be .cabal, not the name.

Browsing the source tree I find a stack.yaml file, so let’s try building with stack instead.

$ stack build
[... hundreds of screens scroll by ...]
    Process exited with code: ExitFailure 1
Progress 4/49

OK so that seemed to be going well …until it didn’t.

Buildkite to the rescue?

At the top of the project I see a Buildkite icon saying the build is passing!

Buildkite usually shows all the build steps in its log, so let’s check it out and copy the same instructions and it should work, right?

Buildkite required me to create an account to access. Fine. So, 5 minutes later:

The cardano-node directory is not found under the public IOHK Buildkite listings.

So either Buildkite isn’t actually building cardano-node or IOHK hasn’t made it public. Let’s go up one level on the buildkite.com URL and see what the correct URL is.

OK, so IOHK only makes one Buildkite repository public: the stakepool registry one. The build might be passing on the private repository, I guess.

So I’ve hit a wall. I have no idea how to build cardano-node.

Trying cabal again:

$ cabal build cardano-node
[...outpu....]
cabal: No cabal file found.
Please create a package description file <pkgname>.cabal

No cigar either. I assume it meant v2-build, so let me try that:

$ cabal v2-build cardano-node
Warning: Requested index-state2020-04-01T00:00:00Z is newer than
'hackage.haskell.org'! Falling back to older state (2020-03-31T22:47:49Z).
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: lobemo-scribe-systemd-0.1.0.0 (user goal)
[__1] next goal: libsystemd-journal (dependency of lobemo-scribe-systemd)
[__1] rejecting: libsystemd-journal-1.4.4 (conflict: pkg-config package
libsystemd>=209, not found in the pkg-config database)
[__1] rejecting: libsystemd-journal-1.4.3, libsystemd-journal-1.4.2,
libsystemd-journal-1.4.1, libsystemd-journal-1.4.0, libsystemd-journal-1.3.4,
libsystemd-journal-1.3.3, libsystemd-journal-1.3.1, libsystemd-journal-1.3.0,
libsystemd-journal-1.2.0, libsystemd-journal-1.1.0, libsystemd-journal-1.0.0
(constraint from project config TODO requires >=1.4.4)
[__1] fail (backjumping, conflict set: libsystemd-journal,
lobemo-scribe-systemd)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: libsystemd-journal,
lobemo-scribe-systemd

OK, so that didn’t work. But this time it looks like something I can try to fix. Seems like it can’t find libsystemd. Going back to Ubuntu’s package manager to install that:

$ sudo apt search libsystemd

This succeeded, so I tried to install one of the missing cabal dependencies by hand:

$ cabal install libsystemd-journal

This went fine. Until it didn’t.

cabal: Error: some packages failed to install:
entropy-0.4.1.6-6yYvxJr6pbn5FDLgHz97fz failed during the configure step. The
exception was:
dieVerbatim: user error (cabal: '/usr/bin/ghc' exited with an error:

/tmp/cabal-tmp-13229/entropy-0.4.1.6/dist/setup/setup.hs:3:1: error:
Could not find module ‘Distribution.Simple’
There are files missing in the ‘Cabal-3.2.0.0’ package,
try running 'ghc-pkg check'.
Use -v to see a list of the files searched for.
|
3 | import Distribution.Simple
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

Here, for some reason, I began to suspect that my ghc installation was somehow corrupted or had stale dependencies. Some messages don’t match stuff I’ve been seeing in online help, which probably indicates that I need to update.

A New Beginning

So I decided to restart with a clean slate. I manually deleted ghc, stack, cabal and cleared the nix binary store.

I restarted with a fresh install from ghcup and stack.

$ ~/.ghcup/bin/cabal v2-build cardano-node
Warning: Requested index-state2020-04-01T00:00:00Z is newer than
'hackage.haskell.org'! Falling back to older state (2020-03-31T22:47:49Z).
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: Win32-network-0.1.0.0 (user goal)
[__1] next goal: base (dependency of Win32-network)
[__1] rejecting: base-4.13.0.0/installed-4.13.0.0 (conflict: Win32-network =>
base>=4.5 && <4.13)
[__1] rejecting: base-4.12.0.0, base-4.11.1.0, base-4.11.0.0, base-4.10.1.0,
base-4.10.0.0, base-4.9.1.0, base-4.9.0.0, base-4.8.2.0, base-4.8.1.0,
base-4.8.0.0, base-4.7.0.2, base-4.7.0.1, base-4.7.0.0, base-4.6.0.1,
base-4.6.0.0, base-4.5.1.0, base-4.5.0.0, base-4.4.1.0, base-4.4.0.0,
base-4.3.1.0, base-4.3.0.0, base-4.2.0.2, base-4.2.0.1, base-4.2.0.0,
base-4.1.0.0, base-4.0.0.0, base-3.0.3.2, base-3.0.3.1 (constraint from
non-upgradeable package requires installed instance)
[__1] fail (backjumping, conflict set: Win32-network, base)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: base, Win32-network

So, the stale version suspicion was right, as it passed all the previous obstacles this time. But now we’ve hit a systemic limitation, not just a glitch: it’s looking for Win32-network-0.1.0.0 which is Win32 stuff. Except I’m running linux.

Not good.

Flashback
Now I remember that the first time I tried to compile Daedalus and cardano-sl under linux back in 2017, I faced similar problems. It seems like IOHK develops the Windows versions first.

Win32 Follies

This is the required Win32 package:

source-repository-package
  type: git
  location: https://github.com/input-output-hk/ouroboros-network
  tag: 18ef245af8181cf3bc208b71c7c4f8502137dbbb
  --sha256: 1v0ksjw1fp9md91igya9vlmnvw5z81jylywgwc2svx125k9b79bj
  subdir: Win32-network

So let’s pay a visit to the ouroboros-network repository and take a look. Fortunately, under the Win32-network dir there’s an example application. This might give us a clue about how this library is used.

import           System.Win32 (HANDLE)
import qualified System.Win32.NamedPipes as Win32
import qualified System.Win32 as Win32
import qualified System.Win32.Async as Win32

This is how the Win32 library is used in the sample app. Let’s go back to cardano-node and see what we find by grepping for this library string:

#if defined(mingw32_HOST_OS)
import           Data.Bits ((.|.))

import qualified System.Win32.NamedPipes as Win32.NamedPipes
import qualified System.Win32.Async      as Win32.Async
import qualified System.Win32            as Win32
import           System.IOManager
#else
import           System.Process (createPipe)
import           System.IO (hClose)
#endif

OK, so there’s a conditional pre-processor guard in place. Which means we can probably remove this dependency from the cabal file without breaking the compilation. Let’s try.

% cabal v2-build all
Warning: Requested index-state2020-04-01T00:00:00Z is newer than
'hackage.haskell.org'! Falling back to older state (2020-03-31T22:47:49Z).
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: byron-spec-chain-0.1.0.0 (user goal)
[__1] trying: hashable-1.3.0.0 (dependency of byron-spec-chain)
[__2] trying: base-4.13.0.0/installed-4.13.0.0 (dependency of
byron-spec-chain)
[__3] trying: cardano-sl-x509-3.0.0 (user goal)
[__4] next goal: ip (dependency of cardano-sl-x509)
[__4] rejecting: ip-1.7.1, ip-1.7.0, ip-1.6.0, ip-1.5.1, ip-1.5.0 (constraint
from project config TODO requires <1.5)
[__4] rejecting: ip-1.4.2.1 (conflict: hashable==1.3.0.0, ip => hashable>=1.2
&& <1.3)
[__4] skipping: ip-1.4.2, ip-1.4.1, ip-1.4.0, ip-1.3.0, ip-1.2.1, ip-1.2.0,
ip-1.1.2, ip-1.1.1, ip-1.1.0, ip-1.0.0, ip-0.9.3, ip-0.9.2, ip-0.9.1, ip-0.9,
ip-0.8.7, ip-0.8.6, ip-0.8.5, ip-0.8.4, ip-0.8.3, ip-0.8.1, ip-0.8, ip-0.7,
ip-0.6.2, ip-0.6.1 (has the same characteristics that caused the previous
version to fail: excludes 'hashable' version 1.3.0.0)
[__4] rejecting: ip-0.6.0 (conflict: cardano-sl-x509 => ip==1.4.*)
[__4] skipping: ip-0.5, ip-0.4, ip-0.3, ip-0.2, ip-0.1 (has the same
characteristics that caused the previous version to fail: excluded by
constraint '==1.4.*' from 'cardano-sl-x509')
[__4] fail (backjumping, conflict set: cardano-sl-x509, hashable, ip)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: hashable, base, ip, byron-spec-chain,
cardano-sl-x509
Try running with --minimize-conflict-set to improve the error message.

Alright, so the Win32 issue is gone. But now have a different problem. Looks like it can’t find hashable, base, ip, byron-spec-chain, and cardano-sl-x509. While searching for the solution it occurred to me that I hadn’t tried stack after the Haskell cleanup. So let’s try that and see how far it goes now:

$ stack build
Error: While constructing the build plan, the following exceptions were encountered:

In the dependencies for iohk-monitoring-0.1.10.1:
    Win32-network needed, but the stack configuration has no specified version (no package with that name found, perhaps there is a typo in a
                  package's build-depends or an omission from the stack.yaml packages list?)
needed due to cardano-api-1.11.0 -> iohk-monitoring-0.1.10.1

In the dependencies for ouroboros-network-framework-0.1.0.0:
    Win32-network must match >=0.1 && <0.2, but the stack configuration has no specified version (no package with that name found, perhaps there
                  is a typo in a package's build-depends or an omission from the stack.yaml packages list?)
needed due to cardano-api-1.11.0 -> ouroboros-network-framework-0.1.0.0

Some different approaches to resolving this:


Plan construction failed.

That’s just great. The empty Some different approaches to resolving this: message is especially helpful. So, let’s try removing the Win32 dependency again, like we did with cabal. Oops, that won’t work this time, because these are separate Git repositories. We need to import both iohk-monitoring and ouroboros-network-framework into our project and then edit their stack dependencies locally.

The Breakthrough

So, while I was getting beat up in every conceivable way, I suddenly came across this video about the Friends and Family (F&F) Shelley testnet:

In the video description, there’s a link to the exercise sheet that F&F pool owners are working on. It turns out this exercise sheet involves building cardano-node! And, as you might have guessed, it includes detailed instructions on how to do it straight from the insiders! Let’s try it!

$ git clone https://github.com/input-output-hk/cardano-node.git
$ cd cardano-node
$ git fetch --all --tags
$ git checkout tags/1.10.0
$ git fetch --all --tags
$ git checkout tags/1.10.0
$ cabal install cardano-node cardano-cli

cabal: Could not resolve dependencies:
[__0] trying: Win32-network-0.1.0.0 (user goal)
[__1] next goal: base (dependency of Win32-network)
[__1] rejecting: base-4.13.0.0/installed-4.13.0.0 (conflict: Win32-network =>
base>=4.5 && <4.13)
[__1] rejecting: base-4.12.0.0, base-4.11.1.0, base-4.11.0.0, base-4.10.1.0,
base-4.10.0.0, base-4.9.1.0, base-4.9.0.0, base-4.8.2.0, base-4.8.1.0,
base-4.8.0.0, base-4.7.0.2, base-4.7.0.1, base-4.7.0.0, base-4.6.0.1,
base-4.6.0.0, base-4.5.1.0, base-4.5.0.0, base-4.4.1.0, base-4.4.0.0,
base-4.3.1.0, base-4.3.0.0, base-4.2.0.2, base-4.2.0.1, base-4.2.0.0,
base-4.1.0.0, base-4.0.0.0, base-3.0.3.2, base-3.0.3.1 (constraint from
non-upgradeable package requires installed instance)
[__1] fail (backjumping, conflict set: Win32-network, base)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: base, Win32-network

Boom! It failed again. Let’s try building different tags.

git checkout tags/1.0.0
Previous HEAD position was 76d321b7 Merge #780
HEAD is now at d5294cd0 enable rtsopts
wrek-system-product-name%  cabal install cardano-node cardano-cli

cabal: Could not resolve dependencies:
[__0] trying: canonical-json-0.6.0.0 (user goal)
[__1] trying: base-4.13.0.0/installed-4.13.0.0 (dependency of canonical-json)
[__2] next goal: cardano-config (user goal)
[__2] rejecting: cardano-config-0.1.0.0 (conflict:
base==4.13.0.0/installed-4.13.0.0, cardano-config => base>=4.12 && <4.13)
[__2] fail (backjumping, conflict set: base, cardano-config)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: base, cardano-config, canonical-json

No cigar. Another one:

$ git checkout tags/1.9.0                
Previous HEAD position was 15de948f Merge #440
HEAD is now at 81878d59 Merge #729
$ cabal install cardano-node cardano-cli
cabal: Could not resolve dependencies:
[__0] trying: Win32-network-0.1.0.0 (user goal)
[__1] next goal: base (dependency of Win32-network)
[__1] rejecting: base-4.13.0.0/installed-4.13.0.0 (conflict: Win32-network =>
base>=4.5 && <4.13)
[__1] rejecting: base-4.12.0.0, base-4.11.1.0, base-4.11.0.0, base-4.10.1.0,
base-4.10.0.0, base-4.9.1.0, base-4.9.0.0, base-4.8.2.0, base-4.8.1.0,
base-4.8.0.0, base-4.7.0.2, base-4.7.0.1, base-4.7.0.0, base-4.6.0.1,
base-4.6.0.0, base-4.5.1.0, base-4.5.0.0, base-4.4.1.0, base-4.4.0.0,
base-4.3.1.0, base-4.3.0.0, base-4.2.0.2, base-4.2.0.1, base-4.2.0.0,
base-4.1.0.0, base-4.0.0.0, base-3.0.3.2, base-3.0.3.1 (constraint from
non-upgradeable package requires installed instance)
[__1] fail (backjumping, conflict set: Win32-network, base)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: base, Win32-network

No luck.

Let’s update the Ubuntu packages and retry everything exactly as in the IOHK instructions.

$ sudo apt-get update -y
$  sudo apt-get -y install build-essential pkg-config libffi-dev libgmp-dev libssl-dev libtinfo-dev libsystemd-dev zlib1g-dev make g++ tmux git jq wget libncursesw5 -y
$ wget https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-install-3.2.0.0-x86_64-unknown-linux.tar.xz
$ tar -xf cabal-install-3.2.0.0-x86_64-unknown-linux.tar.xz
$ rm cabal-install-3.2.0.0-x86_64-unknown-linux.tar.xz cabal.sig
$ mkdir -p ~/.local/bin
$ mv cabal ~/.local/bin/
$ cabal update
$ wget https://downloads.haskell.org/~ghc/8.6.5/ghc-8.6.5-x86_64-deb9-linux.tar.xz
$ tar -xf ghc-8.6.5-x86_64-deb9-linux.tar.xz
$ rm ghc-8.6.5-x86_64-deb9-linux.tar.xz
$ cd ghc-8.6.5
$ ./configure
$ ls cardano-node
$ cd cardano-node
$ git fetch --all --tags
$ git checkout tags/1.10.0
$ cabal install cardano-node cardano-cli

Wrote tarball sdist to
/home/you/cryptos/cardano/cardano-node/dist-newstyle/sdist/cardano-config-0.1.0.0.tar.gz
cabal: filepath wildcard 'README.md' does not match any files.

Boom!

It worked. Let’s try to run cardano-node:

$ scripts/mainnet.sh

There it is! Working like a charm!

Running the cardano-node Docker Image

Containers work like magic, especially with projects which have large and complicated build instructions. In this case, we’ve got good news : IOHK provides official Docker images that you can just download and run! The cardano-node Docker image comes with everything pre-built, so you can just pull the image and run it with minimal setup.

Note on cryptocurrency Docker images: Always be extra careful when deploying Docker images for cryptocurrency wallets and full nodes – any cryptocurrency, not Cardano specifically, but the same precaution applies to cardano-node on Docker.

When you download the cardano-node Docker image, double check the spelling to make sure you’re downloading from the inputoutput organization. Do not trust images from non-official sources unless you absolutely trust the source.

First, download cardano-node Docker image:

docker pull inputoutput/cardano-node

Now run the cardano-node Docker image:

docker run -v /data -e NETWORK=mainnet inputoutput/cardano-node

You can also run a more complete version of the command, passing in the topology, config and database path:

docker run -v $PWD/configuration/defaults/byron-mainnet:/configuration \
     inputoutput/cardano-node run \
      --config /configuration/configuration.yaml \
      --topology /configuration/topology.json \
      --database-path /db

Links

IOHK Build Instructions (These work!)

Build and Run Instructions (Neat tutorial.)

Cardano Tutorials Repository

Making a Shelley blockchain from scratch

Cardano Haskell Node at Github

Cardano Node Setup Tutorials

Shelley Stakepool Pioneers Exercise Sheet 1

Exit mobile version