diff --git a/.gitignore b/.gitignore index b68b2a785b..dfe0aae1cd 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ venv # Files for launcher **/.app-compose.json + +# Nix cargo home +.nix-cargo/ diff --git a/README.md b/README.md index 71651b55ec..5dc48e303e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,21 @@ This repository contains the code for the Near MPC node. It is a rewrite of [Nea - **Nearcore Node**: This repository depends on the nearcore node, included as a submodule in the `/libs` directory. - **Other Dependencies**: All other dependencies are handled by Cargo. +## Development Environment (Nix) + +This repository includes a [flake.nix](flake.nix) file that provides a reproducible development environment using Nix. This environment pre-configures the Rust toolchain, LLVM/Clang tools, NEAR CLI tools, and all necessary system dependencies. +Prerequisites + - **Nix**: [Install Nix](https://nixos.org/download/) with [Flakes enabled](https://nixos.wiki/wiki/Flakes) + - **Docker**: While tools are provided in the shell, the Docker daemon must be running separately on your host system. Docker is required for reproducible builds of the MPC contract. + +### Entering the Shell +To activate the development environment, run the following command in the project root: +```shell +nix develop +``` + +Once you enter the shell, your workspace is immediately ready for development. + ## How it works There are two main parts of the binary: NEAR indexer and MPC signing: diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..ed37e096eb --- /dev/null +++ b/flake.lock @@ -0,0 +1,48 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1767892417, + "narHash": "sha256-dhhvQY67aboBk8b0/u0XB6vwHdgbROZT3fJAjyNh5Ww=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767926800, + "narHash": "sha256-x0n73J6ufD/EhDlVdcoAmF0OQHZ+b0a2cKDc8RZyt+o=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "499e9eed88ff9494b6604205b42847e847dfeb91", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..ccdf8a6420 --- /dev/null +++ b/flake.nix @@ -0,0 +1,190 @@ +{ + description = "Rust development environment for NEAR (Workspace)"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + rust-overlay, + }: + let + lib = nixpkgs.lib; + + systems = [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + ]; + + pkgsFor = + system: + import nixpkgs { + inherit system; + overlays = [ rust-overlay.overlays.default ]; + }; + + forAllSystems = f: lib.genAttrs systems (system: f (pkgsFor system)); + + # Helper: compute clang resource include dir for bindgen/wasm. + clangResourceInclude = + llvmPkgs: + let + clangVersion = lib.versions.major llvmPkgs.clang-unwrapped.version; + in + "${llvmPkgs.clang-unwrapped.lib}/lib/clang/${clangVersion}/include"; + + in + { + devShells = forAllSystems ( + pkgs: + let + inherit (pkgs) stdenv; + + llvmPkgs = pkgs.llvmPackages_19; + + rustToolchain = (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml).override { + extensions = [ + "rust-src" + "rust-analyzer" + "clippy" + "rustfmt" + ]; + }; + + # Local NEAR tooling + cargo-near = pkgs.callPackage ./nix/cargo-near.nix { }; + near-cli-rs = pkgs.callPackage ./nix/near-cli-rs.nix { }; + + # Pinned to CI version + cargoTools = pkgs.callPackage ./nix/cargo-tools.nix { }; + + libcDev = lib.getDev stdenv.cc.libc; + + isX86 = stdenv.hostPlatform.isx86_64; + + envCommon = { + # needed for neard's rocksdb build to avoid unsupported CPU features + CXXFLAGS = "-include cstdint" + lib.optionalString isX86 " -msse4.2 -mpclmul"; + + # WASM Toolchain + CC_wasm32_unknown_unknown = "${llvmPkgs.clang-unwrapped}/bin/clang"; + AR_wasm32_unknown_unknown = "${llvmPkgs.llvm}/bin/llvm-ar"; + CFLAGS_wasm32_unknown_unknown = "-I${clangResourceInclude llvmPkgs}"; + + # Bindgen & Paths + LIBCLANG_PATH = "${llvmPkgs.libclang.lib}/lib"; + RUST_SRC_PATH = "${rustToolchain}/lib/rustlib/src/rust/library"; + + BINDGEN_EXTRA_CLANG_ARGS = lib.concatStringsSep " " [ + "-I${clangResourceInclude llvmPkgs}" + "-I${libcDev}/include" + "-fno-stack-protector" + ]; + + PYTHONPATH = "./pytest:./nearcore_pytest:./tee_launcher"; + + # Prevent Cargo from trying to use the system rustup + RUSTUP_TOOLCHAIN = ""; + CARGO_HOME = ".nix-cargo"; + }; + + envDarwin = lib.optionalAttrs stdenv.isDarwin { + # Force build scripts to use Nix wrappers (not host clang) + CC = "${stdenv.cc}/bin/cc"; + CXX = "${stdenv.cc}/bin/c++"; + + # cc crate looks for these first on macOS + CC_aarch64_apple_darwin = "${stdenv.cc}/bin/cc"; + CXX_aarch64_apple_darwin = "${stdenv.cc}/bin/c++"; + + AR = "${stdenv.cc.bintools}/bin/ar"; + RANLIB = "${stdenv.cc.bintools}/bin/ranlib"; + }; + + dockerTools = with pkgs; [ + docker + docker-compose + ]; + + llvmTools = [ + pkgs.pkg-config + llvmPkgs.clang + llvmPkgs.libclang + ]; + + rustTools = with pkgs; [ + rustToolchain + rustPlatform.bindgenHook + ]; + + nearTools = with pkgs; [ + python3Packages.keyring + near-cli-rs + cargo-near + ]; + + miscTools = with pkgs; [ + git + zizmor + binaryen + ]; + + buildLibs = + with pkgs; + [ + openssl + zlib + libiconv + snappy + lz4 + zstd + bzip2 + ] + ++ lib.optionals stdenv.isLinux [ + udev + dbus + ]; + + hardening = [ + "fortify" + ] + ++ lib.optionals stdenv.isDarwin [ + "stackprotector" + "strictoverflow" + "format" + "zerocallusedregs" + ]; + + in + { + default = pkgs.mkShell { + strictDeps = true; + + packages = + dockerTools ++ llvmTools ++ rustTools ++ cargoTools ++ nearTools ++ miscTools ++ buildLibs; + + env = envCommon // envDarwin; + + # Remove the hardening added by nix to fix jmalloc compilation error. + # More info: https://github.com/tikv/jemallocator/issues/108 + hardeningDisable = hardening; + + shellHook = '' + mkdir -p .nix-cargo + export PATH="$PWD/.nix-cargo/bin:$PATH" + printf "\e[32m🦀 NEAR Dev Shell Active\e[0m\n" + ''; + }; + } + ); + }; +} diff --git a/nix/cargo-near.nix b/nix/cargo-near.nix new file mode 100644 index 0000000000..8592046932 --- /dev/null +++ b/nix/cargo-near.nix @@ -0,0 +1,63 @@ +{ + lib, + stdenv, + rustPlatform, + fetchFromGitHub, + pkg-config, + openssl, + udev, + apple-sdk_14, +}: + +rustPlatform.buildRustPackage rec { + pname = "cargo-near"; + version = "0.18.0"; + + src = fetchFromGitHub { + owner = "near"; + repo = "cargo-near"; + rev = "cargo-near-v${version}"; + hash = "sha256-rkqXOfItKO1MmdUFCih6b6g5057iWI6s1JWu/F6r0DY="; + }; + + cargoHash = "sha256-6GoSN+BrM0sw4rqW+6PIPwkTqZsJDyZAHOm78MtOm90="; + + nativeBuildInputs = [ + pkg-config + ]; + + buildInputs = [ + openssl + ] + ++ lib.optionals stdenv.isLinux [ + udev + ] + ++ lib.optionals stdenv.isDarwin [ + apple-sdk_14 + ]; + + env = { + # Prevent the crate from trying to build its own OpenSSL + OPENSSL_NO_VENDOR = 1; + } + // lib.optionalAttrs stdenv.isDarwin { + # Darwin Sonoma / SDK 14 Compatibility + SDKROOT = "${apple-sdk_14}/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"; + MACOSX_DEPLOYMENT_TARGET = "14.0"; + CRATE_CC_NO_DEFAULTS = "1"; + + # Required for crates like wasm-opt-sys that need explicit C++ standard alignment + CMAKE_CXX_STANDARD = "17"; + WASM_OPT_CXX_FLAGS = "-std=c++17 -stdlib=libc++"; + }; + + # skip tests as they require network access + doCheck = false; + + meta = with lib; { + description = "Cargo extension for reproducible builds of NEAR smart contracts"; + homepage = "https://github.com/near/cargo-near"; + platforms = platforms.unix; + mainProgram = "cargo near"; + }; +} diff --git a/nix/cargo-tools.nix b/nix/cargo-tools.nix new file mode 100644 index 0000000000..82f550d713 --- /dev/null +++ b/nix/cargo-tools.nix @@ -0,0 +1,58 @@ +{ + lib, + rustPlatform, + fetchCrate, + pkgs, +}: + +let + buildTool = + { + pname, + version, + hash, + cargoHash, + }: + rustPlatform.buildRustPackage { + inherit pname version; + src = fetchCrate { + inherit pname version hash; + }; + inherit cargoHash; + doCheck = false; + }; +in +[ + (buildTool { + pname = "cargo-shear"; + version = "1.6.0"; + hash = "sha256-i27DQnWCMTZ5og5KE2Fes5S7RZy/P3SwS5aEtTUoRd0="; + cargoHash = "sha256-1FuLkcYezcrzzZ7B+n5r4oY/1BI0QYwjOqUjqWopeS0="; + }) + + (buildTool { + pname = "cargo-nextest"; + version = "0.9.106"; + hash = "sha256-J8S8Xys/GedOBNZ+MDdxtQq8C1rZOj+qpl+78fcvge0="; + cargoHash = "sha256-wDN09EKLB4jvVGNt1PvrdH9Iqe3EozCFI+wUAFjjGlM="; + }) + + (buildTool { + pname = "cargo-sort"; + version = "2.0.2"; + hash = "sha256-U/LakNUSPqj6FmYimi5ZNVJCRiS7zM4Vzvu4Gb3w38Q="; + cargoHash = "sha256-FoFzBf24mNDTRBfFyTEr9Q7sJjUhs0X/XWRGEoierQ4="; + }) + + (buildTool { + pname = "cargo-deny"; + version = "0.18.9"; + hash = "sha256-WnIkb4OXutgufNWpFooKQiJ5TNhamtTsFJu8bWyWeR4="; + cargoHash = "sha256-2u1DQtvjRfwbCXnX70M7drrMEvNsrVxsbikgrnNOkUE="; + }) + + # --- STANDARD NIXPKGS VERSIONS --- + pkgs.cargo-binstall + pkgs.cargo-insta + pkgs.cargo-make +] diff --git a/nix/near-cli-rs.nix b/nix/near-cli-rs.nix new file mode 100644 index 0000000000..80bc4fc484 --- /dev/null +++ b/nix/near-cli-rs.nix @@ -0,0 +1,51 @@ +{ + lib, + rustPlatform, + fetchFromGitHub, + pkg-config, + openssl, + stdenv, + udev, +}: + +rustPlatform.buildRustPackage rec { + pname = "near-cli-rs"; + version = "0.23.5"; + + src = fetchFromGitHub { + owner = "near"; + repo = "near-cli-rs"; + rev = "v${version}"; + hash = "sha256-2m0DkztczxgzwXcPJ4qGj+ThaTzJsKZSvP8WS4dqH6I="; + }; + + cargoHash = "sha256-i430DhFrhVqnZZ0OmC086I4883vxTjW371dSnqyn71k="; + + # nativeBuildInputs: Tools needed on the host to compile the package + nativeBuildInputs = [ + pkg-config + ]; + + # buildInputs: Libraries the resulting binary will link against + buildInputs = [ + openssl + ] + ++ lib.optionals stdenv.isLinux [ + udev + ]; + + env = { + # Prevent the crate from trying to build its own OpenSSL + OPENSSL_NO_VENDOR = 1; + }; + + # skip tests as they require network access + doCheck = false; + + meta = with lib; { + description = "CLI tool for interacting with the NEAR blockchain. Needed for local development and deployment of smart contracts."; + homepage = "https://github.com/near/near-cli-rs"; + platforms = platforms.unix; + mainProgram = "near"; + }; +}