Any tutorials/guides on Solana development?

Hi,

Recently I wanted to give Solana a try and build something. But the experience of setting up the dev environment has been disappointing.

One of the first steps is to install solana CLI. I wanted to do this “the Nix way” so I searched for Nix packages about Solana and all I found was this solana-testnet, which is supposed to package the solana-cli. Although by installing this package the solana-cli command was successfully installed, some other commands like solana-keygen are missing.

I git pulled Solana’s hello-world repo and tried to build it but ran into another issue, about which I already posted an issue on their repo:

> cargo build-bpf --manifest-path=./src/program-rust/Cargo.toml --bpf-out-dir=dist/program

error: no such subcommand: `build-bpf`

So my question is, has anyone successfully got Solana up and running on their NixOS? Could someone please point me to some repos, guides, tutorials, etc? I did a quick internet search but didn’t find too much useful information.

Thanks a lot!

2 Likes

Hi @rollschild, I think most Solana devs don’t use Nix tools, unlike Cardano devs.
At my work, I was actually discouraged from using Nix and they told they won’t check-in nix related stuff to the repo.

If you run Nix on top of a traditional distro, your best bet is to go the non-Nix route.

I run NixOS and here’s what I did:

  1. Install rustup (either globally or in shell.nix/flake.nix).

  2. The solana cli will add bpf toolchain to rustup. I don’t remember which command did this, I searched my history and couldn’t find.

  3. cargo build-bpf, on first run, will auto download bpf-tools to ~/.cache. It then tries to symlink to <path-to-cli>/bin/sdk which in my case was in nix store. Since nix store is readonly, it failed. So I had to provide cargo build-bpf --bpf-sdk ./sdk.

  4. After the download completes, the build-bpf command will fail because of rpath differences. I had to run autopatchelf ~/.cache/solana/<version>/bpf-tools to fix this.

  5. Now you need to symlink scripts folder and env.sh from solana cli folder to ./sdk.

2 Likes

I’m attaching a the flake.nix I used to get solana cli.
It contains references to bpf-tools, but it’s ignored by solana. Solana will download it’s own. I didn’t bother removing it from flake.nix hoping someday I will make Solana use this bpf-tools.

I also tried using rust-overlay at first. Later realized, solana needs rustup so that it can add its bpf toolchain.

My flake.nix:

{
  description = "https://mlabs.slab.com/posts/solana-exercise-g4y1drpb";
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs";
    rust-overlay.url = "github:oxalica/rust-overlay";
    flake-utils.url  = "github:numtide/flake-utils";
  };
  outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system:
    let
      overlays = [ (import rust-overlay) ];
      pkgs = import nixpkgs {
        inherit system overlays;
      };
      solana-cli = {stdenv, fetchurl, fetchzip, lib, autoPatchelfHook, pkgs }: stdenv.mkDerivation rec {
        name = "solana-${version}";
        version = "1.10.3";
        filename = "solana-release-x86_64-unknown-linux-gnu.tar.bz2";
        src = fetchzip {
          url = "https://github.com/solana-labs/solana/releases/download/v${version}/${filename}";
          sha256 = "sha256-jOUCyK7spTeEZz+h2hxfGpDkZ+2pYbyUFzlQ20c1zv4=";
        };
        nativeBuildInputs = [ autoPatchelfHook pkgs.makeWrapper ];
        buildInputs = with pkgs; [
          sgx-sdk
          ocl-icd
          eudev
          rustup
          stdenv.cc.cc
        ];

        installPhase = ''
          mkdir -p $out;
          cp -r bin $out;
          mkdir -p $out/bin/sdk/;
          ln -s "${solana-bpf-tools-pkg}" $out/bin/sdk/bpf;
          chmod 0755 -R $out;
        '';

        meta = with lib; {
          homepage = "https://docs.solana.com/cli/install-solana-cli-tools#download-prebuilt-binaries";
          platforms = platforms.linux;
        };
      };

      solana-bpf-tools = {stdenv, fetchurl, fetchzip, lib, autoPatchelfHook, pkgs }: stdenv.mkDerivation rec {
        name = "solana-bpf-tools-${version}";
        version = "1.23";
        src = fetchzip {
          url = "https://github.com/solana-labs/bpf-tools/releases/download/v${version}/solana-bpf-tools-linux.tar.bz2";
          sha256 = "sha256-4aWBOAOcGviwJ7znGaHbB1ngNzdXqlfDX8gbZtdV1aA=";
          stripRoot = false;
        };

        nativeBuildInputs = [ autoPatchelfHook ];
        buildInputs = with pkgs; [
          zlib
          stdenv.cc.cc
          openssl
        ];

        installPhase = ''
          mkdir -p $out;
          cp -r $src/llvm $out;
          cp -r $src/rust $out;
          chmod 0755 -R $out;
        '';

        meta = with lib; {
          homepage = "https://github.com/solana-labs/bpf-tools/releases";
          platforms = platforms.linux;
        };
      };
      solana-cli-pkg = (pkgs.callPackage solana-cli {});
      solana-bpf-tools-pkg = (pkgs.callPackage solana-bpf-tools {});
    in with pkgs; {
      devShell = mkShell {
        buildInputs = [
          rust-analyzer
          solana-cli-pkg 
          solana-bpf-tools-pkg
          cargo-edit
          rustup
        ];
      };
    }
  );
}

@itsfarseen Thanks so much for your reply.

I’m using this flake. The steps were:

  1. I put this flake.nix in my project root directory
  2. ran nix build. The build was successful and I saw a result directory with all the executables such as solana-install-init, cargo-build-bpf
  3. ran result/bin/solana-install-init stable and succeeded
  4. the output from step 3 asked me to add home/username/.local/share/solana/install/active_release/bin to the $PATH
  5. I added the following in the flake.nix:
        devShell = pkgs.mkShell {
          buildInputs = [
            solana
          ];
          shellHook = ''
            export PATH="/home/rollschild/.local/share/solana/install/active_release/bin:$PATH"
          '';

        };
      }
  1. Then I did nix develop to enter nix shell
  2. in nix shell when I tried those commands such as solana-install-init I got the following error: bash: /home/username/.local/share/solana/install/active_release/bin/solana-install-init: No such file or directory
  3. I followed this to use zsh, however I got the same “command not found” error: solana-install-init: command not found

I’m sure that my $PATH is already updated with this path:

echo $PATH                                                                                                                                                                
/home/username/.local/share/solana/install/active_release/bin:/nix/store/1vzppfh7qm33xvphjz92wd3kfwqk3yjg-bash-interactive-5.1-p16/bin:/nix/store/mb4zc2l0gspl4hq3rqrq1ca4grslcl8d-patchelf-0.14.5/bin:/nix/store/j0lglxsmvrd3zyv9dlabffc0n0xnxbia-gcc-wrapper-10.3.0/bin:/nix/store/b8i09b63y0yy80hqccvzgq647avyicka-gcc-10.3.0/bin:/nix/store/pdh4nkzb44apgdsy8szhc30w44fj2938-glibc-2.34-115-bin/bin:/nix/store/ffzjj89cgfjm4hd950a08hx52j5rhy3m-coreutils-9.0/bin:/nix/store/i7n7d46r58wrwpilm25yh697m3wwsljb-binutils-wrapper-2.35.2/bin:/nix/store/2lqp15axv6rzrah1q586jadxpxrpxn7g-binutils-2.35.2/bin:/nix/store/ffzjj89cgfjm4hd950a08hx52j5rhy3m-coreutils-9.0/bin:/nix/store/xydbxhw48wq05ir7lpfclglapw5r33cq-findutils-4.9.0/bin:/nix/store/khq547jlph84d2ijzhaiwg4xjsmqhjfd-diffutils-3.8/bin:/nix/store/mdb9hnydn9yiaw0fdn8rsi2mdxszbj47-gnused-4.8/bin:/nix/store/721kbxb3cs9rd03jphyi8bjjbfgwgsrg-gnugrep-3.7/bin:/nix/store/xypqyr4h3csi0h4bhinrjdvaimwnrcj5-gawk-5.1.1/bin:/nix/store/56f7shj6smx48zw4nrwadf2jqzzlw9sx-gnutar-1.34/bin:/nix/store/cpz5jz6kajgzzf0kkz3bx0xrhhivmzm3-gzip-1.11/bin:/nix/store/x04f31fmqw5cwf30gsllicdn1hvvxy9v-bzip2-1.0.6.0.2-bin/bin:/nix/store/7fgvskmgxi5f0yxj3scpfn2cw4zi1n2m-gnumake-4.3/bin:/nix/store/ld19k12mf0pa42ff43qs3sizamp09f03-bash-5.1-p16/bin:/nix/store/dgc8x2bqp0ydq8qz26v9na0kwzrs9bsg-patch-2.7.6/bin:/nix/store/4r55m9vwjqjcynmhiqqfcs7rgvvba6rl-xz-5.2.5-bin/bin:/run/wrappers/bin:/home/rollschild/.nix-profile/bin:/etc/profiles/per-user/rollschild/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin

So basically I successfully updated the $PATH, however the executable in that directory

home/rollschild/.local/share/solana/install/active_release/bin

isn’t really executable.

I actually went in this directory and those “executables” are indeed in there but I wasn’t able to run any of them. They just returned command not found error.

 ~/.local/share/solana/install/active_release/bin $ ls -l                                                                                                                                                                                
-rwxr-xr-x 1  cargo-build-bpf
-rwxr-xr-x 1  cargo-test-bpf
drwxr-xr-x 2 deps
drwxr-xr-x 5  perf-libs
-rwxr-xr-x 1  rbpf-cli
drwxr-xr-x 3 sdk
-rwxr-xr-x 1  solana
-rwxr-xr-x 1  solana-bench-tps
-rwxr-xr-x 1  solana-dos
-rwxr-xr-x 1 solana-faucet
-rwxr-xr-x 1  solana-genesis
-rwxr-xr-x 1  solana-gossip
-rwxr-xr-x 1  solana-install
-rwxr-xr-x 1 solana-install-init
-rwxr-xr-x 1  solana-keygen
-rwxr-xr-x 1  solana-ledger-tool
-rwxr-xr-x 1  solana-log-analyzer
-rwxr-xr-x 1  solana-net-shaper
-rwxr-xr-x 1  solana-stake-accounts
-rwxr-xr-x 1  solana-sys-tuner
-rwxr-xr-x 1  solana-test-validator
-rwxr-xr-x 1  solana-tokens
-rwxr-xr-x 1  solana-validator
-rwxr-xr-x 1  solana-watchtower
-rwxr-xr-x 1  spl-token

Do you have any idea where I did wrong? Thanks!

All binaries compiled on other Linux distros need to be patched to be able to run on NixOS.
This has something to do with dynamic linking and library resolution in NixOS.

Command not found error is usually due to this this. It’s having trouble finding the d

In your case, try running patchelf command on the folder containing your executables.

I forgot the flags, and I’m on mobile right now, so can’t look it up; but I think I have given an example in the previous post. See if that works.

Regards,
Farseen

It’s having trouble finding the dynamic loader*

@itsfarseen Thanks so much for the detailed explanation! Looks like currently my issue is, I don’t know how to add the bpf toolchain to rustup:

The solana cli will add bpf toolchain to rustup . I don’t remember which command did this, I searched my history and couldn’t find.

Because based on what you said, I would need to have the bpf toolchain, then run the cargo build-bpf in order to patchelf the commands to the /.cache/solana/<version>/bpf-tools.

I just don’t know how to have this bpf toolchain. Clearly installing solana using the recommended way wouldn’t work on NixOS. I tried to install the crate by doing

cargo install --version 1.4.9 solana-cargo-build-bpf

but this crate had already been yanked so I couldn’t install it this way.

@itsfarseen sorry, one more question: how did you install Solana at the first place? via nixpkgs? or using the shell-curl command that’s recommended on Solana’s doc? Using the shell command failed for me but from what I’ve found this should the correct way and would get rid of all the errors. But I’m not sure how this translates to NixOS.

How did you install Solana at the first place?

Using the flake.nix I shared and running nix develop.

I don’t know how to add the bpf toolchain to rustup.

Here’s what I think is happening:
Once inside the nix shell, you will have cargo-build-bpf in your path.
Cargo will simply redirect cargo build-bpf to cargo-build-bpf.
Everytime cargo-build-bpf is ran, it checks if the bpf toolchain is added in rustup. If it’s not, then it links it from the path you specify in --bpf-sdk.

To test this, I uninstalled the toolchain:

❯ rustup toolchain uninstall bpf
info: uninstalling toolchain 'bpf'
info: toolchain 'bpf' uninstalled

And ran:

❯ cargo build-bpf --bpf-sdk sdk
BPF SDK: /home/farseen/Projects/<project>/sdk
cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: rustup toolchain link bpf /home/farseen/Projects/<project>/sdk/dependencies/bpf-tools/rust
cargo-build-bpf child: cargo +bpf build --target bpfel-unknown-unknown --release
   Compiling regex-syntax v0.6.25
   Compiling atty v0.2.14
   Compiling socket2 v0.4.4
   Compiling termcolor v1.1.3
... snip ...

I’m the author of the mentioned flake and I just wanted to point out that you can’t currently use the custom BPF toolchain with Nix tooling as far as I can tell. It’s just not implemented by any Rustup replacement.

The flake is also a pretty big experiment especially since I don’t use Solana myself. For now I removed solana-install-init from the flake since that command is essentially replaced by Nix here. I’ll also add something to the README to make the status of this repo clearer.

@itsfarseen Thank you so much for the steps. Using your flake.nix and your steps I did have some progress but now I’m stuck in the cargo build-bpf step.

I ran cargo build-bpf --bpf-sdk sdk and the first two steps succeeded, which verified the bpf toolchain, but it failed on the actual compiling phase, inside nix-shell using nix develop:

[rollschild@nixos:~/projects/example-helloworld/src/program-rust]$ cargo build-bpf --bpf-sdk ./sdk
BPF SDK: /home/rollschild/projects/example-helloworld/src/program-rust/sdk
cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: rustup toolchain link bpf /home/rollschild/projects/example-helloworld/src/program-rust/sdk/dependencies/bpf-tools/rust
cargo-build-bpf child: cargo +bpf build --target bpfel-unknown-unknown --release
error: command failed: 'cargo': No such file or directory (os error 2)

Now this cargo command failed really puzzled me. Doing a which cargo clearly gave me the correct path: /nix/store/429wcfhqydl7v3yzcapc1gvkyx6ss5yp-rustup-1.24.3/bin/cargo, so I don’t quite know why this error came from.

Doing a cargo build succeeded under the same directory, which means there is no issue with cargo itself I guess. So I think the issue is cargo-build-bpf cannot seem to find the correct cargo command? I’m just thinking out loud here.

Sorry for the back-and-forth stupid questions. I really appreciate your help!

To provide a bit more context. I installed rustup globally, via nixpkgs. Everything else is via this flake that you shared.

Okay, I fixed this error by adding cargo to the flake.nix:

        buildInputs = [
          rust-analyzer
          solana-cli-pkg 
          solana-bpf-tools-pkg
          cargo-edit
          cargo # this is new
          rustup
        ];

But now I’m facing another error, which is pretty common based on what I’ve seen on the Internet: no such subcommand: +bpf

[rollschild@nixos:~/projects/example-helloworld/src/program-rust]$ cargo build-bpf --bpf-sdk sdk
BPF SDK: /home/rollschild/projects/example-helloworld/src/program-rust/sdk
cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: rustup toolchain link bpf /home/rollschild/projects/example-helloworld/src/program-rust/sdk/dependencies/bpf-tools/rust
cargo-build-bpf child: cargo +bpf build --target bpfel-unknown-unknown --release
error: no such subcommand: `+bpf`

        Did you mean `b`?

@rollschild This is because bpf toolchain got added to rustup, and cargo which was installed separately is not aware of this.

What you need to do is make cargo-build-bpf aware of cargo provided by rustup.

Sorry for the back-and-forth stupid questions. I really appreciate your help!

No probs : )

I installed rustup globally, via nixpkgs. Everything else is via this flake that you shared.

When you do nix develop, your global rustup should get overriden by the one from the flakes. Try running file $(which cargo) to see if it changes.

Also try running this command.

I do remember facing the same error, but I don’t remember how I fixed it. I think it got fixed after adding rustup to the buildInputs.

Did you remove rustup from there?

Did you remove rustup from there?

Actually no. In fact I even added rustup to the buildInputs that were missing it. But didn’t help.

I actually removed my global installation of rustup. Now everything is installed via flake. Outside of nix develop there is no cargo or rustup now.

Ooh I think I remember: Run patchelf on ~/.rustup/toolchains/bpf

Okay. I feel like it’s another stupid question.

I did take a look at ~/.rustup/toolchain and this was the result:

[rollschild@nixos:~/projects/example-helloworld/src/program-rust]$ ls -l ~/.rustup/toolchains/
total 8
lrwxrwxrwx 1 rollschild users   93 Apr 13 20:34 bpf -> /home/rollschild/projects/example-helloworld/src/program-rust/sdk/dependencies/bpf-tools/rust
drwxr-xr-x 7 rollschild users 4096 Apr 12 21:24 nightly-x86_64-unknown-linux-gnu

so here bpf is already symlinked to the local ./sdk. So my question is, I know patchelf sets the dynamic loader, but if bpf is already symlinked to ./sdk/dependencies/bpf-tools/rust, will patchelf make a difference at all? Plus, I’m not really sure how to run this patchelf, since I would need to run this command on executables, not directories, am I right?

Plus, I’m wondering, if it would be better that we put some of those commands into flake.nix, such as inside shellHook, so that we don’t lose it afterwards.

I did find another shell.nix from Discord user @lithdew , whom I didn’t find on Discourse. This user did a PR: cargo-build-bpf: allow sdk path to be set by environment variables, in which he also shared his solana.nix:

with (import <nixpkgs> { });

let
  stdenv = clang7Stdenv;

  solana-bpf-tools = stdenv.mkDerivation {
    name = "solana-bpf-tools";
    version = "v1.15";
    src = fetchTarball {
      name = "solana-bpf-tools-linux";
      url = "https://github.com/solana-labs/bpf-tools/releases/download/${solana-bpf-tools.version}/solana-bpf-tools-linux.tar.bz2";
      sha256 = "1bng5b5nr0rfz737di8whl06mz3zwz0kqzv1qhlglhv6v3mqrccp";
    };
    nativeBuildInputs = [ autoPatchelfHook openssl.dev stdenv.cc.cc ];
    installPhase = "cp -R $src $out";
  };

  solana-bpf-sdk = stdenv.mkDerivation {
    name = "solana-bpf-sdk";
    version = "v1.6.22";
    src = fetchTarball {
      name = "solana-bpf-sdk";
      url = "https://github.com/solana-labs/solana/releases/download/${solana-bpf-sdk.version}/bpf-sdk.tar.bz2";
      sha256 = "1bfih5nxrglmxh0yvgi2mymddfq3b0qcqkz02ifcpflf4kvj0y20";
    };
    nativeBuildInputs = [ autoPatchelfHook openssl.dev stdenv.cc.cc ];
    installPhase = ''
      cp -R $src $out
      chmod 755 $out
      mkdir -p $out $out/dependencies
      ln -s ${solana-bpf-tools} $out/dependencies/bpf-tools
    '';
  };
in
mkShell {
  packages = [
    solana-bpf-tools
    solana-bpf-sdk
    rustup
    pkgconfig
  ];
  buildInputs = [
    openssl.dev
    libudev.dev
    llvmPackages_7.clang
    llvmPackages_7.llvm
    llvmPackages_7.lld
  ];
  shellHook = ''
    export BPF_SDK_PATH="${solana-bpf-sdk}"
    export LIBCLANG_PATH="${llvmPackages_7.libclang.lib}/lib"
    export PATH=$PATH:~/Desktop/solana/bin

    rm -rf ~/.cache/solana*
    mkdir -p ~/.cache/solana/${solana-bpf-tools.version}
    ln -s ${solana-bpf-tools} ~/.cache/solana/${solana-bpf-tools.version}/bpf-tools
  '';
}

I tried to combine this shell nix with the flake you provided but basically got the same bpf subcommand not found error. But I posted here anyway for your reference.

I’m trying to run his shell nix standalone in another git repo, see if it makes a difference.