diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..af3b9a0 --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +use flake + +source_env_if_exists .envrc.local diff --git a/.gitignore b/.gitignore index 5e8862c..d071cd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,14 @@ # Nix result +result-* + +# Haskell / Cabal +dist-newstyle/ +/cabal.project +/cabal.project.* +/.cabal/ +.ghc.environment.* + +# direnv +.direnv/ +.envrc.local diff --git a/README.md b/README.md index e9c8aec..dd58c9e 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Available commands: ``` ❯ testgen-hs generate -g1 -n1 ApplyTxErr_Conway ``` + ```json { "seed": 1731690796, @@ -266,11 +267,55 @@ And then easily reproduce it by providing the same seed that was found: ``` ❯ ( echo 8182068182028200a0 - echo 81820681820481581cc0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456 + echo 81820681820481581cc0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456 ) | testgen-hs deserialize-stream ``` -``` json +```json {"cbor":"8182068182028200a0","haskellRepr":"ApplyTxError (ConwayCertsFailure (WithdrawalsNotInRewardsCERTS (fromList [])) :| [])","json":{"contents":{"contents":{"contents":{"era":"ShelleyBasedEraConway","error":["ConwayCertsFailure (WithdrawalsNotInRewardsCERTS (fromList []))"],"kind":"ShelleyTxValidationError"},"tag":"TxValidationErrorInCardanoMode"},"tag":"TxCmdTxSubmitValidationError"},"tag":"TxSubmitFail"},"typeTag":"ApplyTxError (ConwayEra StandardCrypto)"} {"cbor":"81820681820481581cc0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456","haskellRepr":"ApplyTxError (ConwayWdrlNotDelegatedToDRep (KeyHash {unKeyHash = \"c0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456\"} :| []) :| [])","json":{"contents":{"contents":{"contents":{"era":"ShelleyBasedEraConway","error":["ConwayWdrlNotDelegatedToDRep (KeyHash {unKeyHash = \"c0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456\"} :| [])"],"kind":"ShelleyTxValidationError"},"tag":"TxValidationErrorInCardanoMode"},"tag":"TxCmdTxSubmitValidationError"},"tag":"TxSubmitFail"},"typeTag":"ApplyTxError (ConwayEra StandardCrypto)"} ``` + +## Development – HLS setup + +This project provides a Nix devshell with GHC and HLS. To get full IDE +support you need [`direnv`](https://direnv.net/) with +[`nix-direnv`](https://github.com/nix-community/nix-direnv) so that the +`.envrc` is picked up automatically. See their official documentation for +installation instructions. + +The instructions below use VS Code as an example, but any editor with LSP and +`direnv` support (Neovim, Emacs, etc.) works the same way. The devshell puts +both `haskell-language-server` and `haskell-language-server-wrapper` on +`PATH`, so most editors should pick up HLS automatically. + +### VS Code example + +1. Install the extensions **Haskell** (`haskell.haskell`) and **direnv** + (`mkhl.direnv`). + +2. Tell the Haskell extension to use the HLS provided by the devshell + instead of managing its own. Add this to your VS Code settings: + + ```json + { "haskell.manageHLS": "PATH" } + ``` + + Optionally, enable semantic highlighting (richer colours for types, + type variables, etc.): + + ```json + { "haskell.plugin.semanticTokens.globalOn": true } + ``` + +3. Allow direnv for this project and restart HLS: + + ``` + direnv allow + ``` + + Then in VS Code: `Ctrl+Shift+P` → _Haskell: Restart Haskell LSP Server_. + +> **Note:** The first HLS session takes roughly 10 minutes while `hie-bios` +> builds all dependencies via `cabal v2-repl`. Subsequent loads use the +> cache at `~/.cache/hie-bios/`. diff --git a/flake.lock b/flake.lock index b53c9a6..ae08974 100644 --- a/flake.lock +++ b/flake.lock @@ -17,6 +17,26 @@ "type": "github" } }, + "devshell": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768818222, + "narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=", + "owner": "numtide", + "repo": "devshell", + "rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { @@ -98,6 +118,7 @@ "root": { "inputs": { "cardano-node": "cardano-node", + "devshell": "devshell", "flake-compat": "flake-compat", "flake-parts": "flake-parts", "nix-bundle-exe": "nix-bundle-exe", diff --git a/flake.nix b/flake.nix index 23e2471..6bf1c9d 100644 --- a/flake.nix +++ b/flake.nix @@ -2,14 +2,26 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; - treefmt-nix.url = "github:numtide/treefmt-nix"; - treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; - flake-compat.url = "github:input-output-hk/flake-compat"; - flake-compat.flake = false; - cardano-node.url = "github:IntersectMBO/cardano-node/10.6.2"; - cardano-node.flake = false; # otherwise, +2k dependencies we don’t really use - nix-bundle-exe.url = "github:3noch/nix-bundle-exe"; - nix-bundle-exe.flake = false; + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-compat = { + url = "github:input-output-hk/flake-compat"; + flake = false; + }; + devshell = { + url = "github:numtide/devshell"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + cardano-node = { + url = "github:IntersectMBO/cardano-node/10.6.2"; + flake = false; # otherwise, +2k dependencies we don’t really use + }; + nix-bundle-exe = { + url = "github:3noch/nix-bundle-exe"; + flake = false; + }; }; outputs = inputs: let @@ -18,6 +30,7 @@ inputs.flake-parts.lib.mkFlake {inherit inputs;} ({config, ...}: { imports = [ inputs.treefmt-nix.flakeModule + inputs.devshell.flakeModule ]; flake.internal = @@ -29,15 +42,10 @@ ); systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; - perSystem = { - config, - system, - pkgs, - ... - }: { - packages = let - internal = inputs.self.internal.${system}; - in + perSystem = {system, ...}: let + internal = inputs.self.internal.${system}; + in { + packages = { default = internal.defaultPackage; } @@ -45,13 +53,20 @@ default-x86_64-windows = inputs.self.internal.x86_64-windows.defaultPackage; }); + devshells.default = internal.devShell.new; + treefmt = {pkgs, ...}: { projectRootFile = "flake.nix"; - programs.alejandra.enable = true; # Nix - programs.ormolu.enable = true; # Haskell - programs.cabal-fmt.enable = true; - programs.shfmt.enable = true; - programs.yamlfmt.enable = pkgs.system != "x86_64-darwin"; # a treefmt-nix+yamlfmt bug on Intel Macs + programs = { + alejandra.enable = true; # Nix + ormolu.enable = true; # Haskell + cabal-fmt.enable = true; + prettier.enable = true; # Markdown, JSON, … + shfmt.enable = true; + ruff-check.enable = true; # Python + ruff-format.enable = true; # Python + yamlfmt.enable = pkgs.system != "x86_64-darwin"; # a treefmt-nix+yamlfmt bug on Intel Macs + }; }; }; @@ -60,7 +75,7 @@ testgen-hs = lib.genAttrs (config.systems ++ ["x86_64-windows"]) ( targetSystem: inputs.self.internal.${targetSystem}.hydraPackage ); - inherit (inputs.self) checks; + inherit (inputs.self) checks devShells; }; in allJobs diff --git a/nix/internal.nix b/nix/internal.nix index a9ab471..7ca4b97 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -3,44 +3,134 @@ targetSystem, }: # For now, let's keep all UNIX definitions together, until they diverge more in the future. -assert __elem targetSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" "x86_64-windows"]; let +assert builtins.elem targetSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" "x86_64-windows"]; let buildSystem = if targetSystem != "x86_64-windows" then targetSystem else "x86_64-linux"; pkgs = inputs.nixpkgs.legacyPackages.${buildSystem}; inherit (pkgs) lib; -in rec { - defaultPackage = testgen-hs; - - cardano-node-flake = let + cardano-node-src = let unpatched = inputs.cardano-node; in + if targetSystem != "aarch64-linux" + then unpatched + else + pkgs.runCommand "source" {} '' + cp -r ${unpatched} $out + chmod -R +w $out + cd $out + ${lib.optionalString (targetSystem == "aarch64-linux") '' + echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix + sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix + ''} + ''; + cardano-node-src-for-flake = + if targetSystem != "aarch64-linux" + then cardano-node-src + else { + outPath = toString cardano-node-src; + inherit (inputs.cardano-node) rev shortRev lastModified lastModifiedDate; + }; + cardano-node-flake' = (import inputs.flake-compat {src = cardano-node-src-for-flake;}).defaultNix; + cardano-ledger-src = let + dep-id = cardano-node-flake'.project.${buildSystem}.hsPkgs.cardano-ledger-core.identifier; + dep-tag = "${dep-id.name}-${dep-id.version}"; + in + pkgs.fetchFromGitHub { + name = "cardano-ledger--${dep-tag}"; + owner = "IntersectMBO"; + repo = "cardano-ledger"; + rev = dep-tag; + hash = "sha256-RvnNYY76OhRuC/uP5Lr+HLEKWyMHCWxx+10HlPrH6mQ="; + }; + patched-cardano-ledger-src = pkgs.runCommandNoCC "cardano-ledger-src-patched" {} '' + cp -r ${cardano-ledger-src} $out + chmod -R +w $out + patch -p1 -d $out/libs/cardano-ledger-core -i ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} + patch -p1 -d $out/libs/cardano-ledger-test -i ${./cardano-ledger-test--expose-helpers.diff} + ${lib.optionalString (targetSystem == "x86_64-windows") '' + patch -p1 -d $out/libs/cardano-ledger-test -i ${./cardano-ledger-test--windows-fix.diff} + ''} + ''; + constrained-generators-src = patched-cardano-node-flake'.project.${buildSystem}.hsPkgs.constrained-generators.src; + patched-constrained-generators-src = constrained-generators-src; + cardano-api-src = cardano-node-flake'.project.${buildSystem}.hsPkgs.cardano-api.src; + cardano-api-id = cardano-node-flake'.project.${buildSystem}.hsPkgs.cardano-api.identifier; + # Extract the latest revised .cabal from the local CHaP index. + # CHaP revisions relax version bounds to match the package set; + # without this, the on-disk .cabal carries the original tight bounds + # and cabal-install's solver rejects valid dependency plans. + # + # GNU tar overwrites earlier entries with later ones, so a plain + # extract of a duplicate member yields the last revision — exactly + # the latest CHaP revision we need. + cardano-api-revised-cabal = let + member = "${cardano-api-id.name}/${cardano-api-id.version}/${cardano-api-id.name}.cabal"; + in + pkgs.runCommandNoCC "cardano-api-revised-cabal" {} '' + tar xf ${cardano-node-flake'.inputs.CHaP}/01-index.tar.gz '${member}' + cp '${member}' $out + ''; + patched-cardano-api-src = pkgs.applyPatches { + name = "cardano-api-src-patched"; + src = cardano-api-src; + prePatch = '' + cp ${cardano-api-revised-cabal} cardano-api.cabal + ''; + patches = [./cardano-api--expose-internal.diff]; + }; + patched-cardano-node-src = {withOurCode ? true}: + pkgs.runCommandNoCC "cardano-node-src-patched" {} '' + cp -r ${cardano-node-src} $out + chmod -R +w $out + cd $out + ${lib.optionalString (targetSystem == "aarch64-linux") '' + echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix + sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix + ''} + ${ + if withOurCode + then '' + cp -r ${../testgen-hs} ./testgen-hs + '' + else '' + mkdir -p ./testgen-hs + '' + } + sed -r '/^packages:/ a\ testgen-hs' -i cabal.project + + patch -p1 -i ${./cardano-node--apply-patches.diff} + cp ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} nix/cardano-ledger-core--Arbitrary-PoolMetadata.diff + cp ${./cardano-ledger-test--expose-helpers.diff} nix/cardano-ledger-test--expose-helpers.diff + cp ${ + if targetSystem == "x86_64-windows" + then ./cardano-ledger-test--windows-fix.diff + else pkgs.emptyFile + } nix/cardano-ledger-test--windows-fix.diff + cp ${./cardano-api--expose-internal.diff} nix/cardano-api--expose-internal.diff + + patch -p1 -i ${./cardano-node--expose-cardano-ledger-test.diff} + sed -r 's,CARDANO_LEDGER_SOURCE,${cardano-ledger-src},g' -i nix/haskell.nix + + patch -p1 -i ${./cardano-node--export-cardano-submit-api.diff} + ''; + patched-cardano-node-flake' = (import inputs.flake-compat { - src = - if targetSystem != "aarch64-linux" - then unpatched - else { - outPath = toString (pkgs.runCommand "source" {} '' - cp -r ${unpatched} $out - chmod -R +w $out - cd $out - ${lib.optionalString (targetSystem == "aarch64-linux") '' - echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix - sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix - ''} - ''); - inherit (unpatched) rev shortRev lastModified lastModifiedDate; - }; + src = { + outPath = toString (patched-cardano-node-src {withOurCode = true;}); + inherit (inputs.cardano-node) rev shortRev lastModified lastModifiedDate; + }; }) .defaultNix; +in rec { + defaultPackage = testgen-hs; + cardano-node-flake = cardano-node-flake'; cardano-node-packages = { x86_64-linux = cardano-node-flake.hydraJobs.x86_64-linux.musl; - aarch64-linux = cardano-node-flake.packages.aarch64-linux; - x86_64-darwin = cardano-node-flake.packages.x86_64-darwin; - aarch64-darwin = cardano-node-flake.packages.aarch64-darwin; + inherit (cardano-node-flake.packages) aarch64-linux x86_64-darwin aarch64-darwin; } .${ targetSystem @@ -48,53 +138,198 @@ in rec { inherit (cardano-node-packages) cardano-node cardano-cli; - testgen-hs = let - patched-flake = let - unpatched = inputs.cardano-node; - cardano-ledger-src = let - dep-id = cardano-node-flake.project.${buildSystem}.hsPkgs.cardano-ledger-core.identifier; - dep-tag = "${dep-id.name}-${dep-id.version}"; - in - pkgs.fetchFromGitHub { - name = "cardano-ledger--${dep-tag}"; - owner = "IntersectMBO"; - repo = "cardano-ledger"; - rev = dep-tag; - hash = "sha256-RvnNYY76OhRuC/uP5Lr+HLEKWyMHCWxx+10HlPrH6mQ="; - }; + devShell = let + cardano-node-devshell = cardano-node-flake.devShells.${buildSystem}.default; + cardano-node-inputs = lib.filter lib.isDerivation ( + (cardano-node-devshell.buildInputs or []) + ++ (cardano-node-devshell.nativeBuildInputs or []) + ++ (cardano-node-devshell.propagatedBuildInputs or []) + ++ (cardano-node-devshell.propagatedNativeBuildInputs or []) + ); + cardano-node-env = pkgs.buildEnv { + name = "cardano-node-devshell-env"; + paths = cardano-node-inputs; + ignoreCollisions = true; + }; + cardano-node-ghc-libdir = cardano-node-devshell.NIX_GHC_LIBDIR or ""; + + # Extract an individual package from the cardano-node devshell inputs + # so we can expose it as a devshell command with its own menu entry. + findInput = pred: label: + lib.findFirst pred (throw "devshell input '${label}' not found") cardano-node-inputs; + + haskell-language-server = findInput (p: (p.pname or "") == "haskell-language-server-exe-haskell-language-server") "haskell-language-server"; + + # Shim so that editors looking for haskell-language-server-wrapper find it + # on PATH; the devshell already sets the correct GHC. + haskell-language-server-wrapper = pkgs.writeShellApplication { + name = "haskell-language-server-wrapper"; + text = '' + exec ${lib.getExe haskell-language-server} "$@" + ''; + }; + + # numtide/devshell does not run stdenv setup hooks, so env vars that + # mkShell would set (e.g. PKG_CONFIG_PATH) are missing. + # PKG_CONFIG_PATH is critical: without it the cabal solver cannot + # verify pkgconfig-depends and dependency resolution fails. + # We extract it by running the same stdenv setup with the same inputs. + devshell-pkg-config-path = + pkgs.runCommand "devshell-pkg-config-path" { + inherit (cardano-node-devshell) buildInputs nativeBuildInputs; + propagatedBuildInputs = cardano-node-devshell.propagatedBuildInputs or []; + propagatedNativeBuildInputs = cardano-node-devshell.propagatedNativeBuildInputs or []; + } '' + source $stdenv/setup 2>/dev/null || true + mkdir -p $out + echo -n "''${PKG_CONFIG_PATH:-}" >$out/PKG_CONFIG_PATH + ''; + + cabal-project-base = cardano-node-flake.project.${buildSystem}.args.cabalProject; + cabal-project-template = pkgs.writeText "cabal.project" cabal-project-base; + cabal-project-extra-packages = [ + "${patched-cardano-api-src}" + "${patched-cardano-ledger-src}/libs/cardano-ledger-core" + "${patched-cardano-ledger-src}/libs/cardano-ledger-test" + "${patched-constrained-generators-src}" + "@REPO_ROOT@/testgen-hs" + ]; + cabal-project-extra-packages-json = builtins.toJSON cabal-project-extra-packages; + # cardano-ledger-core uses the deprecated PV3.fromGHC (should be + # fromHaskellRatio). The upstream cabal.project sets -Werror globally, + # which promotes this deprecation warning into a build error. The nix + # build is unaffected because haskell.nix doesn't pass -Werror from + # the project file, but the devshell's cabal does. + cabal-project-extra-suffix = '' + package cardano-ledger-core + ghc-options: -Wwarn=deprecations + ''; + cabal-project-rewrite-script = pkgs.substituteAll { + src = ./rewrite_cabal_project.py; + cabal_project_template = toString cabal-project-template; + patched_node_src = toString (patched-cardano-node-src {withOurCode = false;}); + extra_packages_json = cabal-project-extra-packages-json; + extra_project_suffix = cabal-project-extra-suffix; + local_chap_path = toString cardano-node-flake'.inputs.CHaP; + repo_root = "@REPO_ROOT@"; + }; + + # Pre-generate cabal.project at Nix build time so the devshell + # startup doesn't need to launch Python (~260 ms saved). + # The only remaining placeholder is @REPO_ROOT@, which is + # sed-replaced with $PRJ_ROOT at shell entry. + cabal-project-generated = + pkgs.runCommand "cabal-project-generated" { + nativeBuildInputs = [pkgs.python3]; + } '' + python3 ${cabal-project-rewrite-script} + cp cabal.project $out + ''; + in { + old = let + chap-store-path = toString cardano-node-flake'.inputs.CHaP; in - (import inputs.flake-compat { - src = { - outPath = toString (pkgs.runCommandNoCC "source" {} '' - cp -r ${unpatched} $out - chmod -R +w $out - cd $out - ${lib.optionalString (targetSystem == "aarch64-linux") '' - echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix - sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix - ''} - cp -r ${../testgen-hs} ./testgen-hs - sed -r '/^packages:/ a\ testgen-hs' -i cabal.project - - patch -p1 -i ${./cardano-node--apply-patches.diff} - cp ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} nix/cardano-ledger-core--Arbitrary-PoolMetadata.diff - cp ${./cardano-ledger-test--expose-helpers.diff} nix/cardano-ledger-test--expose-helpers.diff - cp ${ - if targetSystem == "x86_64-windows" - then ./cardano-ledger-test--windows-fix.diff - else pkgs.emptyFile - } nix/cardano-ledger-test--windows-fix.diff - cp ${./cardano-api--expose-internal.diff} nix/cardano-api--expose-internal.diff - - patch -p1 -i ${./cardano-node--expose-cardano-ledger-test.diff} - sed -r 's,CARDANO_LEDGER_SOURCE,${cardano-ledger-src},g' -i nix/haskell.nix - - patch -p1 -i ${./cardano-node--export-cardano-submit-api.diff} - ''); - inherit (unpatched) rev shortRev lastModified lastModifiedDate; - }; - }) - .defaultNix; + pkgs.mkShell { + inputsFrom = [cardano-node-devshell]; + shellHook = '' + export CABAL_DIR="$PWD/.cabal" + _new=$(sed "s|@REPO_ROOT@|$PWD|g" ${cabal-project-generated}) + if [ ! -e "$PWD/cabal.project" ] || [ "$(cat "$PWD/cabal.project")" != "$_new" ]; then + printf '%s\n' "$_new" > "$PWD/cabal.project" + fi + + _chap_marker="$CABAL_DIR/.chap-store-path" + if [ ! -e "$_chap_marker" ] || [ "$(cat "$_chap_marker")" != "${chap-store-path}" ]; then + mkdir -p "$CABAL_DIR/packages/cardano-haskell-packages" + cabal update cardano-haskell-packages 2>/dev/null + printf '%s' '${chap-store-path}' >"$_chap_marker" + fi + + if [ ! -e "$CABAL_DIR/packages/hackage.haskell.org/01-index.tar" ]; then + echo "First-time setup: downloading Hackage package index…" + cabal update hackage.haskell.org + fi + ''; + }; + new = {config, ...}: { + name = "testgen-hs-devshell"; + env = + lib.optional (cardano-node-ghc-libdir != "") { + name = "NIX_GHC_LIBDIR"; + value = cardano-node-ghc-libdir; + } + ++ [ + { + name = "CABAL_DIR"; + eval = "$PRJ_ROOT/.cabal"; + } + { + name = "PKG_CONFIG_PATH"; + eval = "$(cat ${devshell-pkg-config-path}/PKG_CONFIG_PATH)\${PKG_CONFIG_PATH:+:\$PKG_CONFIG_PATH}"; + } + ]; + commands = [ + { + name = "ghc"; + package = findInput (p: lib.hasPrefix "ghc-shell-for-packages" (p.name or "")) "ghc"; + category = "haskell"; + } + { + name = "cabal"; + package = findInput (p: (p.pname or "") == "cabal-install-exe-cabal") "cabal"; + category = "haskell"; + } + { + name = "haskell-language-server"; + package = haskell-language-server; + category = "haskell"; + } + ]; + devshell = { + packages = [ + cardano-node-env + haskell-language-server-wrapper + ]; + startup.rewrite-cabal-project.text = let + chap-store-path = toString cardano-node-flake'.inputs.CHaP; + in '' + _new=$(sed "s|@REPO_ROOT@|$PRJ_ROOT|g" ${cabal-project-generated}) + if [ ! -e "$PRJ_ROOT/cabal.project" ] || [ "$(cat "$PRJ_ROOT/cabal.project")" != "$_new" ]; then + printf '%s\n' "$_new" > "$PRJ_ROOT/cabal.project" + fi + # Re-index the local CHaP only when the underlying Nix store + # path changes (i.e. after a flake.lock update). A marker + # file records the store path that was last indexed. + _chap_marker="$CABAL_DIR/.chap-store-path" + if [ ! -e "$_chap_marker" ] || [ "$(cat "$_chap_marker")" != "${chap-store-path}" ]; then + mkdir -p "$CABAL_DIR/packages/cardano-haskell-packages" + cabal update cardano-haskell-packages 2>/dev/null + printf '%s' '${chap-store-path}' >"$_chap_marker" + fi + + # On first use of this project-local CABAL_DIR, also fetch the + # Hackage index so that 'cabal build' works out of the box. + if [ ! -e "$CABAL_DIR/packages/hackage.haskell.org/01-index.tar" ]; then + echo "First-time setup: downloading Hackage package index…" + cabal update hackage.haskell.org + fi + + ''; + motd = '' + + {202}🔨 Welcome to ${config.name}{reset} + $(menu) + + You can now run: + · {bold}cabal build testgen-hs{reset} + · {bold}cabal run testgen-hs -- --help{reset} + ''; + }; + }; + }; + + testgen-hs = let + patched-flake = patched-cardano-node-flake'; in { x86_64-linux = patched-flake.hydraJobs.x86_64-linux.musl.testgen-hs; diff --git a/nix/rewrite_cabal_project.py b/nix/rewrite_cabal_project.py new file mode 100644 index 0000000..e9e0cae --- /dev/null +++ b/nix/rewrite_cabal_project.py @@ -0,0 +1,91 @@ +"""Rewrite cabal.project packages for the devshell.""" + +import json +import re +from pathlib import Path + + +TEMPLATE_PATH = Path("@cabal_project_template@") +PATCHED_NODE_SRC = "@patched_node_src@" +EXTRA_PACKAGES = json.loads(r"""@extra_packages_json@""") +EXTRA_PROJECT_SUFFIX = r"""@extra_project_suffix@""" +LOCAL_CHAP_PATH = "@local_chap_path@" +REPO_ROOT = "@repo_root@" + +REPO_NAME = "cardano-haskell-packages" + + +def rewrite_packages(text: str) -> str: + """Rewrite the packages section to use patched and extra packages.""" + lines = text.splitlines() + out = [] + i = 0 + while i < len(lines): + line = lines[i] + if line.strip() != "packages:": + out.append(line) + i += 1 + continue + + out.append(line) + i += 1 + block_lines = [] + while i < len(lines): + next_line = lines[i] + if next_line and not next_line.startswith(" "): + break + block_lines.append(next_line) + i += 1 + + trailing_blanks = [] + while block_lines and block_lines[-1].strip() == "": + trailing_blanks.insert(0, block_lines.pop()) + + for block_line in block_lines: + stripped = block_line.strip() + if stripped == "" or stripped.startswith("--"): + out.append(block_line) + continue + entry = stripped + if entry.startswith("/"): + out.append(f" {entry}") + else: + out.append(f" {PATCHED_NODE_SRC}/{entry}") + + for pkg in EXTRA_PACKAGES: + out.append(f" {pkg.replace('@REPO_ROOT@', REPO_ROOT)}") + + out.extend(trailing_blanks) + + result = "\n".join(out) + if text.endswith("\n"): + result += "\n" + return result + + +def use_local_chap(text: str) -> str: + """Point the CHaP repository at a local Nix-store copy. + + This uses the same pinned CHaP snapshot that cardano-node's haskell.nix + uses, giving us cabal-file revisions and full reproducibility. + """ + # Replace the "repository cardano-haskell-packages" block (url, secure, + # root-keys, and any key-threshold lines) with a local file: URL. + text = re.sub( + r"(?:--[^\n]*\n)*" # optional preceding comment lines + r"repository cardano-haskell-packages\n" + r"(?: [^\n]+\n)*", # indented continuation lines + f"repository {REPO_NAME}\n url: file:{LOCAL_CHAP_PATH}\n secure: True\n", + text, + ) + + return text + + +base_text = TEMPLATE_PATH.read_text(encoding="utf-8") +result = rewrite_packages(base_text) +if LOCAL_CHAP_PATH.strip(): + result = use_local_chap(result) +if EXTRA_PROJECT_SUFFIX.strip(): + result = result.rstrip("\n") + "\n\n" + EXTRA_PROJECT_SUFFIX.strip() + "\n" +Path("cabal.project").write_text(result, encoding="utf-8") diff --git a/testgen-hs/SynthEvalTx.hs b/testgen-hs/SynthEvalTx.hs index 0e6145c..1411cf0 100644 --- a/testgen-hs/SynthEvalTx.hs +++ b/testgen-hs/SynthEvalTx.hs @@ -42,7 +42,7 @@ import Data.Aeson import qualified Data.Aeson as J import qualified Data.Aeson.Encoding as AesonEncoding import qualified Data.ByteString as BS -import Data.FileEmbed (embedFile) +import Data.FileEmbed (embedFile, makeRelativeToProject) import qualified Data.Map.Strict as Map import qualified Data.Set as Set import Data.Text (Text) @@ -135,7 +135,7 @@ dummySystemStart = SystemStart $ UTCTime (fromGregorian 2020 7 29) (secondsToDiffTime 0) protocolParamsJSON :: BS.ByteString -protocolParamsJSON = $(embedFile "protocol-params-preview.json") +protocolParamsJSON = $(embedFile =<< makeRelativeToProject "protocol-params-preview.json") protocolParams :: PParams (Cardano.Ledger.Api.Era.ConwayEra) protocolParams = diff --git a/testgen-hs/protocol-params-preview.json b/testgen-hs/protocol-params-preview.json index 744c658..0aa0009 100644 --- a/testgen-hs/protocol-params-preview.json +++ b/testgen-hs/protocol-params-preview.json @@ -4,648 +4,58 @@ "committeeMinSize": 0, "costModels": { "PlutusV1": [ - 100788, - 420, - 1, - 1, - 1000, - 173, - 0, - 1, - 1000, - 59957, - 4, - 1, - 11183, - 32, - 201305, - 8356, - 4, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 100, - 100, - 16000, - 100, - 94375, - 32, - 132994, - 32, - 61462, - 4, - 72010, - 178, - 0, - 1, - 22151, - 32, - 91189, - 769, - 4, - 2, - 85848, - 228465, - 122, - 0, - 1, - 1, - 1000, - 42921, - 4, - 2, - 24548, - 29498, - 38, - 1, - 898148, - 27279, - 1, - 51775, - 558, - 1, - 39184, - 1000, - 60594, - 1, - 141895, - 32, - 83150, - 32, - 15299, - 32, - 76049, - 1, - 13169, - 4, - 22100, - 10, - 28999, - 74, - 1, - 28999, - 74, - 1, - 43285, - 552, - 1, - 44749, - 541, - 1, - 33852, - 32, - 68246, - 32, - 72362, - 32, - 7243, - 32, - 7391, - 32, - 11546, - 32, - 85848, - 228465, - 122, - 0, - 1, - 1, - 90434, - 519, - 0, - 1, - 74433, - 32, - 85848, - 228465, - 122, - 0, - 1, - 1, - 85848, - 228465, - 122, - 0, - 1, - 1, - 270652, - 22588, - 4, - 1457325, - 64566, - 4, - 20467, - 1, - 4, - 0, - 141992, - 32, - 100788, - 420, - 1, - 1, - 81663, - 32, - 59498, - 32, - 20142, - 32, - 24588, - 32, - 20744, - 32, - 25933, - 32, - 24623, - 32, - 53384111, - 14333, - 10 + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, + 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, + 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, + 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, + 42921, 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, + 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, + 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, + 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, + 228465, 122, 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, + 1, 1, 85848, 228465, 122, 0, 1, 1, 270652, 22588, 4, 1457325, 64566, 4, + 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, + 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, 53384111, 14333, 10 ], "PlutusV2": [ - 100788, - 420, - 1, - 1, - 1000, - 173, - 0, - 1, - 1000, - 59957, - 4, - 1, - 11183, - 32, - 201305, - 8356, - 4, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 100, - 100, - 16000, - 100, - 94375, - 32, - 132994, - 32, - 61462, - 4, - 72010, - 178, - 0, - 1, - 22151, - 32, - 91189, - 769, - 4, - 2, - 85848, - 228465, - 122, - 0, - 1, - 1, - 1000, - 42921, - 4, - 2, - 24548, - 29498, - 38, - 1, - 898148, - 27279, - 1, - 51775, - 558, - 1, - 39184, - 1000, - 60594, - 1, - 141895, - 32, - 83150, - 32, - 15299, - 32, - 76049, - 1, - 13169, - 4, - 22100, - 10, - 28999, - 74, - 1, - 28999, - 74, - 1, - 43285, - 552, - 1, - 44749, - 541, - 1, - 33852, - 32, - 68246, - 32, - 72362, - 32, - 7243, - 32, - 7391, - 32, - 11546, - 32, - 85848, - 228465, - 122, - 0, - 1, - 1, - 90434, - 519, - 0, - 1, - 74433, - 32, - 85848, - 228465, - 122, - 0, - 1, - 1, - 85848, - 228465, - 122, - 0, - 1, - 1, - 955506, - 213312, - 0, - 2, - 270652, - 22588, - 4, - 1457325, - 64566, - 4, - 20467, - 1, - 4, - 0, - 141992, - 32, - 100788, - 420, - 1, - 1, - 81663, - 32, - 59498, - 32, - 20142, - 32, - 24588, - 32, - 20744, - 32, - 25933, - 32, - 24623, - 32, - 43053543, - 10, - 53384111, - 14333, - 10, - 43574283, - 26308, - 10 + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, + 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, + 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, + 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, + 42921, 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, + 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, + 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, + 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, + 228465, 122, 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, + 1, 1, 85848, 228465, 122, 0, 1, 1, 955506, 213312, 0, 2, 270652, 22588, 4, + 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, 81663, + 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, + 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10 ], "PlutusV3": [ - 100788, - 420, - 1, - 1, - 1000, - 173, - 0, - 1, - 1000, - 59957, - 4, - 1, - 11183, - 32, - 201305, - 8356, - 4, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 100, - 100, - 16000, - 100, - 94375, - 32, - 132994, - 32, - 61462, - 4, - 72010, - 178, - 0, - 1, - 22151, - 32, - 91189, - 769, - 4, - 2, - 85848, - 123203, - 7305, - -900, - 1716, - 549, - 57, - 85848, - 0, - 1, - 1, - 1000, - 42921, - 4, - 2, - 24548, - 29498, - 38, - 1, - 898148, - 27279, - 1, - 51775, - 558, - 1, - 39184, - 1000, - 60594, - 1, - 141895, - 32, - 83150, - 32, - 15299, - 32, - 76049, - 1, - 13169, - 4, - 22100, - 10, - 28999, - 74, - 1, - 28999, - 74, - 1, - 43285, - 552, - 1, - 44749, - 541, - 1, - 33852, - 32, - 68246, - 32, - 72362, - 32, - 7243, - 32, - 7391, - 32, - 11546, - 32, - 85848, - 123203, - 7305, - -900, - 1716, - 549, - 57, - 85848, - 0, - 1, - 90434, - 519, - 0, - 1, - 74433, - 32, - 85848, - 123203, - 7305, - -900, - 1716, - 549, - 57, - 85848, - 0, - 1, - 1, - 85848, - 123203, - 7305, - -900, - 1716, - 549, - 57, - 85848, - 0, - 1, - 955506, - 213312, - 0, - 2, - 270652, - 22588, - 4, - 1457325, - 64566, - 4, - 20467, - 1, - 4, - 0, - 141992, - 32, - 100788, - 420, - 1, - 1, - 81663, - 32, - 59498, - 32, - 20142, - 32, - 24588, - 32, - 20744, - 32, - 25933, - 32, - 24623, - 32, - 43053543, - 10, - 53384111, - 14333, - 10, - 43574283, - 26308, - 10, - 16000, - 100, - 16000, - 100, - 962335, - 18, - 2780678, - 6, - 442008, - 1, - 52538055, - 3756, - 18, - 267929, - 18, - 76433006, - 8868, - 18, - 52948122, - 18, - 1995836, - 36, - 3227919, - 12, - 901022, - 1, - 166917843, - 4307, - 36, - 284546, - 36, - 158221314, - 26549, - 36, - 74698472, - 36, - 333849714, - 1, - 254006273, - 72, - 2174038, - 72, - 2261318, - 64571, - 4, - 207616, - 8310, - 4, - 1293828, - 28716, - 63, - 0, - 1, - 1006041, - 43623, - 251, - 0, - 1, - 100181, - 726, - 719, - 0, - 1, - 100181, - 726, - 719, - 0, - 1, - 100181, - 726, - 719, - 0, - 1, - 107878, - 680, - 0, - 1, - 95336, - 1, - 281145, - 18848, - 0, - 1, - 180194, - 159, - 1, - 1, - 158519, - 8942, - 0, - 1, - 159378, - 8813, - 0, - 1, - 107490, - 3298, - 1, - 106057, - 655, - 1, - 1964219, - 24520, - 3 + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, + 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, + 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, + 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, 123203, 7305, -900, 1716, + 549, 57, 85848, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148, + 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, + 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, + 43285, 552, 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, + 7391, 32, 11546, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, + 1, 90434, 519, 0, 1, 74433, 32, 85848, 123203, 7305, -900, 1716, 549, 57, + 85848, 0, 1, 1, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, + 955506, 213312, 0, 2, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, + 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, + 20744, 32, 25933, 32, 24623, 32, 43053543, 10, 53384111, 14333, 10, + 43574283, 26308, 10, 16000, 100, 16000, 100, 962335, 18, 2780678, 6, + 442008, 1, 52538055, 3756, 18, 267929, 18, 76433006, 8868, 18, 52948122, + 18, 1995836, 36, 3227919, 12, 901022, 1, 166917843, 4307, 36, 284546, 36, + 158221314, 26549, 36, 74698472, 36, 333849714, 1, 254006273, 72, 2174038, + 72, 2261318, 64571, 4, 207616, 8310, 4, 1293828, 28716, 63, 0, 1, 1006041, + 43623, 251, 0, 1, 100181, 726, 719, 0, 1, 100181, 726, 719, 0, 1, 100181, + 726, 719, 0, 1, 107878, 680, 0, 1, 95336, 1, 281145, 18848, 0, 1, 180194, + 159, 1, 1, 158519, 8942, 0, 1, 159378, 8813, 0, 1, 107490, 3298, 1, + 106057, 655, 1, 1964219, 24520, 3 ] }, "dRepActivity": 31, @@ -683,7 +93,7 @@ "maxValueSize": 5000, "minFeeRefScriptCostPerByte": 15, "minPoolCost": 170000000, - "monetaryExpansion": 0.0030, + "monetaryExpansion": 0.003, "poolPledgeInfluence": 0.3, "poolRetireMaxEpoch": 18, "poolVotingThresholds": {