diff --git a/flake.lock b/flake.lock index f78c13ea85..06433dcefe 100644 --- a/flake.lock +++ b/flake.lock @@ -17,6 +17,40 @@ "type": "github" } }, + "CHaP_2": { + "flake": false, + "locked": { + "lastModified": 1777742585, + "narHash": "sha256-ZzXz2vOhqethlqPgBExPXEnKWvaTbidsIxh5MGv+pwE=", + "owner": "intersectmbo", + "repo": "cardano-haskell-packages", + "rev": "e8a483522ee73c8c9493ea6055553e5c2532e66b", + "type": "github" + }, + "original": { + "owner": "intersectmbo", + "ref": "repo", + "repo": "cardano-haskell-packages", + "type": "github" + } + }, + "CHaP_3": { + "flake": false, + "locked": { + "lastModified": 1779876270, + "narHash": "sha256-FA9E1EaQvPITpO/8weQyi7p3KHgyNb9GiwM6F96Aoeo=", + "owner": "intersectmbo", + "repo": "cardano-haskell-packages", + "rev": "cb63b6483a5d6ce36fb07815736315bd4408162e", + "type": "github" + }, + "original": { + "owner": "intersectmbo", + "ref": "repo", + "repo": "cardano-haskell-packages", + "type": "github" + } + }, "HTTP": { "flake": false, "locked": { @@ -33,6 +67,38 @@ "type": "github" } }, + "HTTP_2": { + "flake": false, + "locked": { + "lastModified": 1451647621, + "narHash": "sha256-oHIyw3x0iKBexEo49YeUDV1k74ZtyYKGR2gNJXXRxts=", + "owner": "phadej", + "repo": "HTTP", + "rev": "9bc0996d412fef1787449d841277ef663ad9a915", + "type": "github" + }, + "original": { + "owner": "phadej", + "repo": "HTTP", + "type": "github" + } + }, + "HTTP_3": { + "flake": false, + "locked": { + "lastModified": 1451647621, + "narHash": "sha256-oHIyw3x0iKBexEo49YeUDV1k74ZtyYKGR2gNJXXRxts=", + "owner": "phadej", + "repo": "HTTP", + "rev": "9bc0996d412fef1787449d841277ef663ad9a915", + "type": "github" + }, + "original": { + "owner": "phadej", + "repo": "HTTP", + "type": "github" + } + }, "blst": { "flake": false, "locked": { @@ -50,6 +116,57 @@ "type": "github" } }, + "blst_2": { + "flake": false, + "locked": { + "lastModified": 1749204514, + "narHash": "sha256-Q9/zGN93TnJt2c8YvSaURstoxT02ts3nVkO5V08m4TI=", + "owner": "supranational", + "repo": "blst", + "rev": "6d960cd05d6fe2b5bc9ba161edf0c1a131b87c4c", + "type": "github" + }, + "original": { + "owner": "supranational", + "ref": "v0.3.15", + "repo": "blst", + "type": "github" + } + }, + "blst_3": { + "flake": false, + "locked": { + "lastModified": 1749204514, + "narHash": "sha256-Q9/zGN93TnJt2c8YvSaURstoxT02ts3nVkO5V08m4TI=", + "owner": "supranational", + "repo": "blst", + "rev": "6d960cd05d6fe2b5bc9ba161edf0c1a131b87c4c", + "type": "github" + }, + "original": { + "owner": "supranational", + "ref": "v0.3.15", + "repo": "blst", + "type": "github" + } + }, + "cabal-32": { + "flake": false, + "locked": { + "lastModified": 1603716527, + "narHash": "sha256-X0TFfdD4KZpwl0Zr6x+PLxUt/VyKQfX7ylXHdmZIL+w=", + "owner": "haskell", + "repo": "cabal", + "rev": "48bf10787e27364730dd37a42b603cee8d6af7ee", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.2", + "repo": "cabal", + "type": "github" + } + }, "cabal-34": { "flake": false, "locked": { @@ -67,6 +184,40 @@ "type": "github" } }, + "cabal-34_2": { + "flake": false, + "locked": { + "lastModified": 1645834128, + "narHash": "sha256-wG3d+dOt14z8+ydz4SL7pwGfe7SiimxcD/LOuPCV6xM=", + "owner": "haskell", + "repo": "cabal", + "rev": "5ff598c67f53f7c4f48e31d722ba37172230c462", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.4", + "repo": "cabal", + "type": "github" + } + }, + "cabal-34_3": { + "flake": false, + "locked": { + "lastModified": 1645834128, + "narHash": "sha256-wG3d+dOt14z8+ydz4SL7pwGfe7SiimxcD/LOuPCV6xM=", + "owner": "haskell", + "repo": "cabal", + "rev": "5ff598c67f53f7c4f48e31d722ba37172230c462", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.4", + "repo": "cabal", + "type": "github" + } + }, "cabal-36": { "flake": false, "locked": { @@ -84,406 +235,1482 @@ "type": "github" } }, - "cardano-shell": { + "cabal-36_2": { "flake": false, "locked": { - "lastModified": 1608537748, - "narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=", + "lastModified": 1669081697, + "narHash": "sha256-I5or+V7LZvMxfbYgZATU4awzkicBwwok4mVoje+sGmU=", + "owner": "haskell", + "repo": "cabal", + "rev": "8fd619e33d34924a94e691c5fea2c42f0fc7f144", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.6", + "repo": "cabal", + "type": "github" + } + }, + "cabal-36_3": { + "flake": false, + "locked": { + "lastModified": 1669081697, + "narHash": "sha256-I5or+V7LZvMxfbYgZATU4awzkicBwwok4mVoje+sGmU=", + "owner": "haskell", + "repo": "cabal", + "rev": "8fd619e33d34924a94e691c5fea2c42f0fc7f144", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.6", + "repo": "cabal", + "type": "github" + } + }, + "cardano-automation": { + "inputs": { + "flake-utils": "flake-utils", + "hackageNix": "hackageNix", + "haskellNix": [ + "cardano-node", + "haskellNix" + ], + "nixpkgs": [ + "cardano-node", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1764682512, + "narHash": "sha256-yY3yIBmiCKsZ7YN++ttEKZiVMIHjjlAngFWaTGvBBvg=", "owner": "input-output-hk", - "repo": "cardano-shell", - "rev": "9392c75087cb9a3d453998f4230930dea3a95725", + "repo": "cardano-automation", + "rev": "9a91636c94317bff98ebf6b913f8c38beef0b374", "type": "github" }, "original": { "owner": "input-output-hk", - "repo": "cardano-shell", + "repo": "cardano-automation", "type": "github" } }, - "flake-compat": { - "flake": false, + "cardano-automation_2": { + "inputs": { + "flake-utils": "flake-utils_2", + "hackageNix": "hackageNix_3", + "haskellNix": [ + "cardano-node-tx-generator", + "haskellNix" + ], + "nixpkgs": [ + "cardano-node-tx-generator", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1672831974, - "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", + "lastModified": 1764682512, + "narHash": "sha256-yY3yIBmiCKsZ7YN++ttEKZiVMIHjjlAngFWaTGvBBvg=", "owner": "input-output-hk", - "repo": "flake-compat", - "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", + "repo": "cardano-automation", + "rev": "9a91636c94317bff98ebf6b913f8c38beef0b374", "type": "github" }, "original": { "owner": "input-output-hk", - "ref": "hkm/gitlab-fix", - "repo": "flake-compat", + "repo": "cardano-automation", "type": "github" } }, - "flake-parts": { + "cardano-node": { "inputs": { - "nixpkgs-lib": "nixpkgs-lib" + "CHaP": "CHaP_2", + "cardano-automation": "cardano-automation", + "customConfig": "customConfig", + "empty-flake": "empty-flake", + "flake-compat": "flake-compat", + "hackageNix": "hackageNix_2", + "haskellNix": "haskellNix", + "incl": "incl", + "iohkNix": "iohkNix", + "nixpkgs": [ + "cardano-node", + "haskellNix", + "nixpkgs-unstable" + ], + "utils": "utils" }, "locked": { - "lastModified": 1778716662, - "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", + "lastModified": 1777953209, + "narHash": "sha256-+dod+EL73MICgbgflyJnwDlxbCeaBbBLrr2tLX59hAA=", + "owner": "IntersectMBO", + "repo": "cardano-node", + "rev": "97036a66bcf8c89f687ae57a048eecc0389977ef", "type": "github" }, "original": { - "owner": "hercules-ci", - "repo": "flake-parts", + "owner": "IntersectMBO", + "ref": "11.0.1", + "repo": "cardano-node", "type": "github" } }, - "hackage-for-stackage": { + "cardano-node-tx-generator": { + "inputs": { + "CHaP": "CHaP_3", + "cardano-automation": "cardano-automation_2", + "customConfig": "customConfig_2", + "empty-flake": "empty-flake_2", + "flake-compat": "flake-compat_3", + "hackageNix": "hackageNix_4", + "haskellNix": "haskellNix_2", + "incl": "incl_2", + "iohkNix": "iohkNix_2", + "nixpkgs": [ + "cardano-node-tx-generator", + "haskellNix", + "nixpkgs-unstable" + ], + "utils": "utils_2" + }, + "locked": { + "lastModified": 1785180201, + "narHash": "sha256-sq4JYPuxVPwh6oE/o8nlPOnrK4CJc1CPGSnVvrR6AlU=", + "owner": "IntersectMBO", + "repo": "cardano-node", + "rev": "ec672fd082c7839792d0ec4a0bce27e96c9bb70f", + "type": "github" + }, + "original": { + "owner": "IntersectMBO", + "repo": "cardano-node", + "rev": "ec672fd082c7839792d0ec4a0bce27e96c9bb70f", + "type": "github" + } + }, + "cardano-shell": { "flake": false, "locked": { - "lastModified": 1778287713, - "narHash": "sha256-YeF0MpAeY9NSj5QZaUi6PARuNw+zACmaMe0/ylfkiic=", + "lastModified": 1608537748, + "narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=", "owner": "input-output-hk", - "repo": "hackage.nix", - "rev": "cdfa372f1eab438209ee785486e6e951b598b795", + "repo": "cardano-shell", + "rev": "9392c75087cb9a3d453998f4230930dea3a95725", "type": "github" }, "original": { "owner": "input-output-hk", - "ref": "for-stackage", - "repo": "hackage.nix", + "repo": "cardano-shell", "type": "github" } }, - "hackage-internal": { + "cardano-shell_2": { "flake": false, "locked": { - "lastModified": 1750307553, - "narHash": "sha256-iiafNoeLHwlSLQTyvy8nPe2t6g5AV4PPcpMeH/2/DLs=", + "lastModified": 1608537748, + "narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=", "owner": "input-output-hk", - "repo": "hackage.nix", - "rev": "f7867baa8817fab296528f4a4ec39d1c7c4da4f3", + "repo": "cardano-shell", + "rev": "9392c75087cb9a3d453998f4230930dea3a95725", "type": "github" }, "original": { "owner": "input-output-hk", - "repo": "hackage.nix", + "repo": "cardano-shell", "type": "github" } }, - "hackageNix": { + "cardano-shell_3": { "flake": false, "locked": { - "lastModified": 1779824738, - "narHash": "sha256-o4W05VYZ0hlIA/BXxL6hYsQ9HL6JsawxepFnCh429R8=", + "lastModified": 1608537748, + "narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=", "owner": "input-output-hk", - "repo": "hackage.nix", - "rev": "decbe184cfce30ea802ea95c874511f776a4b48a", + "repo": "cardano-shell", + "rev": "9392c75087cb9a3d453998f4230930dea3a95725", "type": "github" }, "original": { "owner": "input-output-hk", - "repo": "hackage.nix", + "repo": "cardano-shell", "type": "github" } }, - "haskellNix": { - "inputs": { - "HTTP": "HTTP", - "cabal-34": "cabal-34", - "cabal-36": "cabal-36", - "cardano-shell": "cardano-shell", - "flake-compat": "flake-compat", - "hackage": [ - "hackageNix" - ], - "hackage-for-stackage": "hackage-for-stackage", - "hackage-internal": "hackage-internal", - "hls": "hls", - "hls-1.10": "hls-1.10", - "hls-2.0": "hls-2.0", - "hls-2.10": "hls-2.10", - "hls-2.11": "hls-2.11", - "hls-2.12": "hls-2.12", - "hls-2.2": "hls-2.2", - "hls-2.3": "hls-2.3", - "hls-2.4": "hls-2.4", - "hls-2.5": "hls-2.5", - "hls-2.6": "hls-2.6", - "hls-2.7": "hls-2.7", - "hls-2.8": "hls-2.8", - "hls-2.9": "hls-2.9", - "hpc-coveralls": "hpc-coveralls", - "iserv-proxy": "iserv-proxy", - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-2305": "nixpkgs-2305", - "nixpkgs-2311": "nixpkgs-2311", - "nixpkgs-2405": "nixpkgs-2405", - "nixpkgs-2411": "nixpkgs-2411", - "nixpkgs-2505": "nixpkgs-2505", - "nixpkgs-2511": "nixpkgs-2511", - "nixpkgs-unstable": "nixpkgs-unstable", - "old-ghc-nix": "old-ghc-nix", - "stackage": "stackage" - }, + "customConfig": { "locked": { - "lastModified": 1778289300, - "narHash": "sha256-3r+uzTzU/BcepGz7fn8ZQ+n/BMUsMXroh1LesvVNjdg=", + "lastModified": 1630400035, + "narHash": "sha256-MWaVOCzuFwp09wZIW9iHq5wWen5C69I940N1swZLEQ0=", "owner": "input-output-hk", - "repo": "haskell.nix", - "rev": "3971e5c801475512678e53989bfda1b53b86f76b", + "repo": "empty-flake", + "rev": "2040a05b67bf9a669ce17eca56beb14b4206a99a", "type": "github" }, "original": { "owner": "input-output-hk", - "repo": "haskell.nix", + "repo": "empty-flake", "type": "github" } }, - "hls": { - "flake": false, + "customConfig_2": { "locked": { - "lastModified": 1741604408, - "narHash": "sha256-tuq3+Ip70yu89GswZ7DSINBpwRprnWnl6xDYnS4GOsc=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "682d6894c94087da5e566771f25311c47e145359", + "lastModified": 1630400035, + "narHash": "sha256-MWaVOCzuFwp09wZIW9iHq5wWen5C69I940N1swZLEQ0=", + "owner": "input-output-hk", + "repo": "empty-flake", + "rev": "2040a05b67bf9a669ce17eca56beb14b4206a99a", "type": "github" }, "original": { - "owner": "haskell", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "repo": "empty-flake", "type": "github" } }, - "hls-1.10": { - "flake": false, + "empty-flake": { "locked": { - "lastModified": 1680000865, - "narHash": "sha256-rc7iiUAcrHxwRM/s0ErEsSPxOR3u8t7DvFeWlMycWgo=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "b08691db779f7a35ff322b71e72a12f6e3376fd9", + "lastModified": 1630400035, + "narHash": "sha256-MWaVOCzuFwp09wZIW9iHq5wWen5C69I940N1swZLEQ0=", + "owner": "input-output-hk", + "repo": "empty-flake", + "rev": "2040a05b67bf9a669ce17eca56beb14b4206a99a", "type": "github" }, "original": { - "owner": "haskell", - "ref": "1.10.0.0", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "repo": "empty-flake", "type": "github" } }, - "hls-2.0": { - "flake": false, + "empty-flake_2": { "locked": { - "lastModified": 1687698105, - "narHash": "sha256-OHXlgRzs/kuJH8q7Sxh507H+0Rb8b7VOiPAjcY9sM1k=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "783905f211ac63edf982dd1889c671653327e441", + "lastModified": 1630400035, + "narHash": "sha256-MWaVOCzuFwp09wZIW9iHq5wWen5C69I940N1swZLEQ0=", + "owner": "input-output-hk", + "repo": "empty-flake", + "rev": "2040a05b67bf9a669ce17eca56beb14b4206a99a", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.0.0.1", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "repo": "empty-flake", "type": "github" } }, - "hls-2.10": { + "flake-compat": { "flake": false, "locked": { - "lastModified": 1743069404, - "narHash": "sha256-q4kDFyJDDeoGqfEtrZRx4iqMVEC2MOzCToWsFY+TOzY=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "2318c61db3a01e03700bd4b05665662929b7fe8b", + "lastModified": 1647532380, + "narHash": "sha256-wswAxyO8AJTH7d5oU8VK82yBCpqwA+p6kLgpb1f1PAY=", + "owner": "input-output-hk", + "repo": "flake-compat", + "rev": "7da118186435255a30b5ffeabba9629c344c0bec", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.10.0.0", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "ref": "fixes", + "repo": "flake-compat", "type": "github" } }, - "hls-2.11": { + "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1747306193, - "narHash": "sha256-/MmtpF8+FyQlwfKHqHK05BdsxC9LHV70d/FiMM7pzBM=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "46ef4523ea4949f47f6d2752476239f1c6d806fe", + "lastModified": 1672831974, + "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", + "owner": "input-output-hk", + "repo": "flake-compat", + "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.11.0.0", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "ref": "hkm/gitlab-fix", + "repo": "flake-compat", "type": "github" } }, - "hls-2.12": { + "flake-compat_3": { "flake": false, "locked": { - "lastModified": 1758709460, - "narHash": "sha256-xkI8MIIVEVARskfWbGAgP5sHG/lyeKnkm0LIOJ19X5w=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "7d983de4fa7ff54369f6dd31444bdb9869aec83e", + "lastModified": 1647532380, + "narHash": "sha256-wswAxyO8AJTH7d5oU8VK82yBCpqwA+p6kLgpb1f1PAY=", + "owner": "input-output-hk", + "repo": "flake-compat", + "rev": "7da118186435255a30b5ffeabba9629c344c0bec", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.12.0.0", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "ref": "fixes", + "repo": "flake-compat", "type": "github" } }, - "hls-2.2": { + "flake-compat_4": { "flake": false, "locked": { - "lastModified": 1693064058, - "narHash": "sha256-8DGIyz5GjuCFmohY6Fa79hHA/p1iIqubfJUTGQElbNk=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "b30f4b6cf5822f3112c35d14a0cba51f3fe23b85", + "lastModified": 1672831974, + "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", + "owner": "input-output-hk", + "repo": "flake-compat", + "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.2.0.0", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "ref": "hkm/gitlab-fix", + "repo": "flake-compat", "type": "github" } }, - "hls-2.3": { + "flake-compat_5": { "flake": false, "locked": { - "lastModified": 1695910642, - "narHash": "sha256-tR58doOs3DncFehHwCLczJgntyG/zlsSd7DgDgMPOkI=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "458ccdb55c9ea22cd5d13ec3051aaefb295321be", + "lastModified": 1672831974, + "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", + "owner": "input-output-hk", + "repo": "flake-compat", + "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.3.0.0", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "ref": "hkm/gitlab-fix", + "repo": "flake-compat", "type": "github" } }, - "hls-2.4": { - "flake": false, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, "locked": { - "lastModified": 1699862708, - "narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57", + "lastModified": 1778716662, + "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.4.0.1", - "repo": "haskell-language-server", + "owner": "hercules-ci", + "repo": "flake-parts", "type": "github" } }, - "hls-2.5": { - "flake": false, + "flake-utils": { "locked": { - "lastModified": 1701080174, - "narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "27f8c3d3892e38edaef5bea3870161815c4d014c", + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.5.0.0", - "repo": "haskell-language-server", + "owner": "numtide", + "repo": "flake-utils", "type": "github" } }, - "hls-2.6": { - "flake": false, + "flake-utils_2": { "locked": { - "lastModified": 1705325287, - "narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e", + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.6.0.0", - "repo": "haskell-language-server", + "owner": "numtide", + "repo": "flake-utils", "type": "github" } }, - "hls-2.7": { + "hackage-for-stackage": { "flake": false, "locked": { - "lastModified": 1708965829, - "narHash": "sha256-LfJ+TBcBFq/XKoiNI7pc4VoHg4WmuzsFxYJ3Fu+Jf+M=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "50322b0a4aefb27adc5ec42f5055aaa8f8e38001", + "lastModified": 1755649550, + "narHash": "sha256-YNKeqYIezur2MvPmfVI/aHjcVRwOdBW7Du3jg6iXjKs=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "5e56db8bc478dfb7466ea83744c3ab928aff0329", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.7.0.0", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "ref": "for-stackage", + "repo": "hackage.nix", "type": "github" } }, - "hls-2.8": { + "hackage-for-stackage_2": { "flake": false, "locked": { - "lastModified": 1715153580, - "narHash": "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "dd1be1beb16700de59e0d6801957290bcf956a0a", + "lastModified": 1782348961, + "narHash": "sha256-9wVcuwjT+3GNmYt7JWBUuyfBXVNdp+jvhsjen+Zvcwk=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "afa4af51502c5973e8a2529a35bda4a5e39a7ea8", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.8.0.0", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "ref": "for-stackage", + "repo": "hackage.nix", "type": "github" } }, - "hls-2.9": { + "hackage-for-stackage_3": { "flake": false, "locked": { - "lastModified": 1719993701, - "narHash": "sha256-wy348++MiMm/xwtI9M3vVpqj2qfGgnDcZIGXw8sF1sA=", - "owner": "haskell", - "repo": "haskell-language-server", - "rev": "90319a7e62ab93ab65a95f8f2bcf537e34dae76a", + "lastModified": 1778287713, + "narHash": "sha256-YeF0MpAeY9NSj5QZaUi6PARuNw+zACmaMe0/ylfkiic=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "cdfa372f1eab438209ee785486e6e951b598b795", "type": "github" }, "original": { - "owner": "haskell", - "ref": "2.9.0.1", - "repo": "haskell-language-server", + "owner": "input-output-hk", + "ref": "for-stackage", + "repo": "hackage.nix", "type": "github" } }, - "hpc-coveralls": { + "hackage-internal": { "flake": false, "locked": { - "lastModified": 1607498076, - "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", - "owner": "sevanspowell", - "repo": "hpc-coveralls", - "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", + "lastModified": 1750307553, + "narHash": "sha256-iiafNoeLHwlSLQTyvy8nPe2t6g5AV4PPcpMeH/2/DLs=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "f7867baa8817fab296528f4a4ec39d1c7c4da4f3", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "hackage-internal_2": { + "flake": false, + "locked": { + "lastModified": 1750307553, + "narHash": "sha256-iiafNoeLHwlSLQTyvy8nPe2t6g5AV4PPcpMeH/2/DLs=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "f7867baa8817fab296528f4a4ec39d1c7c4da4f3", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "hackage-internal_3": { + "flake": false, + "locked": { + "lastModified": 1750307553, + "narHash": "sha256-iiafNoeLHwlSLQTyvy8nPe2t6g5AV4PPcpMeH/2/DLs=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "f7867baa8817fab296528f4a4ec39d1c7c4da4f3", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "hackageNix": { + "flake": false, + "locked": { + "lastModified": 1759154585, + "narHash": "sha256-OC5Y3E20bwkfMVlB2uhf7eF/FcuC1JD/BXkxR8rMjR4=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "7e61ac3eb4cc042b37c6511b65984f59fe6d40de", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "hackageNix_2": { + "flake": false, + "locked": { + "lastModified": 1774557316, + "narHash": "sha256-AErDLAypo/a4gNSKjExpNUM7xdlsTdTf68cWnbr1bOA=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "06fa3e96f4d7ced3496ec984c8016aad5282db67", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "hackageNix_3": { + "flake": false, + "locked": { + "lastModified": 1759154585, + "narHash": "sha256-OC5Y3E20bwkfMVlB2uhf7eF/FcuC1JD/BXkxR8rMjR4=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "7e61ac3eb4cc042b37c6511b65984f59fe6d40de", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "hackageNix_4": { + "flake": false, + "locked": { + "lastModified": 1778061448, + "narHash": "sha256-cPUF8+l1ej7x4UZcuuf6IDsxU1WWmGWC0vFBH+6jXZk=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "ba6ab6f3b781c8f308cba4fa384eafa48033f3cc", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "hackageNix_5": { + "flake": false, + "locked": { + "lastModified": 1779824738, + "narHash": "sha256-o4W05VYZ0hlIA/BXxL6hYsQ9HL6JsawxepFnCh429R8=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "decbe184cfce30ea802ea95c874511f776a4b48a", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "haskellNix": { + "inputs": { + "HTTP": "HTTP", + "cabal-32": "cabal-32", + "cabal-34": "cabal-34", + "cabal-36": "cabal-36", + "cardano-shell": "cardano-shell", + "flake-compat": "flake-compat_2", + "hackage": [ + "cardano-node", + "hackageNix" + ], + "hackage-for-stackage": "hackage-for-stackage", + "hackage-internal": "hackage-internal", + "hls": "hls", + "hls-1.10": "hls-1.10", + "hls-2.0": "hls-2.0", + "hls-2.10": "hls-2.10", + "hls-2.11": "hls-2.11", + "hls-2.2": "hls-2.2", + "hls-2.3": "hls-2.3", + "hls-2.4": "hls-2.4", + "hls-2.5": "hls-2.5", + "hls-2.6": "hls-2.6", + "hls-2.7": "hls-2.7", + "hls-2.8": "hls-2.8", + "hls-2.9": "hls-2.9", + "hpc-coveralls": "hpc-coveralls", + "iserv-proxy": "iserv-proxy", + "nixpkgs": [ + "cardano-node", + "nixpkgs" + ], + "nixpkgs-2305": "nixpkgs-2305", + "nixpkgs-2311": "nixpkgs-2311", + "nixpkgs-2405": "nixpkgs-2405", + "nixpkgs-2411": "nixpkgs-2411", + "nixpkgs-2505": "nixpkgs-2505", + "nixpkgs-unstable": "nixpkgs-unstable", + "old-ghc-nix": "old-ghc-nix", + "stackage": "stackage" + }, + "locked": { + "lastModified": 1762315551, + "narHash": "sha256-7uaB/UpiFn/+gf7s5NMpSTTUv5Ws30DjsmmqZry+1cY=", + "owner": "input-output-hk", + "repo": "haskell.nix", + "rev": "ef52c36b9835c77a255befe2a20075ba71e3bfab", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "haskell.nix", + "type": "github" + } + }, + "haskellNix_2": { + "inputs": { + "HTTP": "HTTP_2", + "cabal-34": "cabal-34_2", + "cabal-36": "cabal-36_2", + "cardano-shell": "cardano-shell_2", + "flake-compat": "flake-compat_4", + "hackage": [ + "cardano-node-tx-generator", + "hackageNix" + ], + "hackage-for-stackage": "hackage-for-stackage_2", + "hackage-internal": "hackage-internal_2", + "hls": "hls_2", + "hls-1.10": "hls-1.10_2", + "hls-2.0": "hls-2.0_2", + "hls-2.10": "hls-2.10_2", + "hls-2.11": "hls-2.11_2", + "hls-2.12": "hls-2.12", + "hls-2.2": "hls-2.2_2", + "hls-2.3": "hls-2.3_2", + "hls-2.4": "hls-2.4_2", + "hls-2.5": "hls-2.5_2", + "hls-2.6": "hls-2.6_2", + "hls-2.7": "hls-2.7_2", + "hls-2.8": "hls-2.8_2", + "hls-2.9": "hls-2.9_2", + "hpc-coveralls": "hpc-coveralls_2", + "iserv-proxy": "iserv-proxy_2", + "nixpkgs": [ + "cardano-node-tx-generator", + "nixpkgs" + ], + "nixpkgs-2305": "nixpkgs-2305_2", + "nixpkgs-2311": "nixpkgs-2311_2", + "nixpkgs-2405": "nixpkgs-2405_2", + "nixpkgs-2411": "nixpkgs-2411_2", + "nixpkgs-2505": "nixpkgs-2505_2", + "nixpkgs-2511": "nixpkgs-2511", + "nixpkgs-unstable": "nixpkgs-unstable_2", + "old-ghc-nix": "old-ghc-nix_2", + "stackage": "stackage_2" + }, + "locked": { + "lastModified": 1782375089, + "narHash": "sha256-icdawbfIVT+g6FshBQ/yCFsfzXF//0qjOQzhg0ANweE=", + "owner": "input-output-hk", + "repo": "haskell.nix", + "rev": "bfee90426aa5761fa67cbb4e5c39e525e34a3a9b", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "haskell.nix", + "type": "github" + } + }, + "haskellNix_3": { + "inputs": { + "HTTP": "HTTP_3", + "cabal-34": "cabal-34_3", + "cabal-36": "cabal-36_3", + "cardano-shell": "cardano-shell_3", + "flake-compat": "flake-compat_5", + "hackage": [ + "hackageNix" + ], + "hackage-for-stackage": "hackage-for-stackage_3", + "hackage-internal": "hackage-internal_3", + "hls": "hls_3", + "hls-1.10": "hls-1.10_3", + "hls-2.0": "hls-2.0_3", + "hls-2.10": "hls-2.10_3", + "hls-2.11": "hls-2.11_3", + "hls-2.12": "hls-2.12_2", + "hls-2.2": "hls-2.2_3", + "hls-2.3": "hls-2.3_3", + "hls-2.4": "hls-2.4_3", + "hls-2.5": "hls-2.5_3", + "hls-2.6": "hls-2.6_3", + "hls-2.7": "hls-2.7_3", + "hls-2.8": "hls-2.8_3", + "hls-2.9": "hls-2.9_3", + "hpc-coveralls": "hpc-coveralls_3", + "iserv-proxy": "iserv-proxy_3", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-2305": "nixpkgs-2305_3", + "nixpkgs-2311": "nixpkgs-2311_3", + "nixpkgs-2405": "nixpkgs-2405_3", + "nixpkgs-2411": "nixpkgs-2411_3", + "nixpkgs-2505": "nixpkgs-2505_3", + "nixpkgs-2511": "nixpkgs-2511_2", + "nixpkgs-unstable": "nixpkgs-unstable_3", + "old-ghc-nix": "old-ghc-nix_3", + "stackage": "stackage_3" + }, + "locked": { + "lastModified": 1778289300, + "narHash": "sha256-3r+uzTzU/BcepGz7fn8ZQ+n/BMUsMXroh1LesvVNjdg=", + "owner": "input-output-hk", + "repo": "haskell.nix", + "rev": "3971e5c801475512678e53989bfda1b53b86f76b", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "haskell.nix", + "type": "github" + } + }, + "hls": { + "flake": false, + "locked": { + "lastModified": 1741604408, + "narHash": "sha256-tuq3+Ip70yu89GswZ7DSINBpwRprnWnl6xDYnS4GOsc=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "682d6894c94087da5e566771f25311c47e145359", + "type": "github" + }, + "original": { + "owner": "haskell", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-1.10": { + "flake": false, + "locked": { + "lastModified": 1680000865, + "narHash": "sha256-rc7iiUAcrHxwRM/s0ErEsSPxOR3u8t7DvFeWlMycWgo=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "b08691db779f7a35ff322b71e72a12f6e3376fd9", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "1.10.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-1.10_2": { + "flake": false, + "locked": { + "lastModified": 1680000865, + "narHash": "sha256-rc7iiUAcrHxwRM/s0ErEsSPxOR3u8t7DvFeWlMycWgo=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "b08691db779f7a35ff322b71e72a12f6e3376fd9", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "1.10.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-1.10_3": { + "flake": false, + "locked": { + "lastModified": 1680000865, + "narHash": "sha256-rc7iiUAcrHxwRM/s0ErEsSPxOR3u8t7DvFeWlMycWgo=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "b08691db779f7a35ff322b71e72a12f6e3376fd9", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "1.10.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.0": { + "flake": false, + "locked": { + "lastModified": 1687698105, + "narHash": "sha256-OHXlgRzs/kuJH8q7Sxh507H+0Rb8b7VOiPAjcY9sM1k=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "783905f211ac63edf982dd1889c671653327e441", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.0.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.0_2": { + "flake": false, + "locked": { + "lastModified": 1687698105, + "narHash": "sha256-OHXlgRzs/kuJH8q7Sxh507H+0Rb8b7VOiPAjcY9sM1k=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "783905f211ac63edf982dd1889c671653327e441", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.0.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.0_3": { + "flake": false, + "locked": { + "lastModified": 1687698105, + "narHash": "sha256-OHXlgRzs/kuJH8q7Sxh507H+0Rb8b7VOiPAjcY9sM1k=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "783905f211ac63edf982dd1889c671653327e441", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.0.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.10": { + "flake": false, + "locked": { + "lastModified": 1743069404, + "narHash": "sha256-q4kDFyJDDeoGqfEtrZRx4iqMVEC2MOzCToWsFY+TOzY=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "2318c61db3a01e03700bd4b05665662929b7fe8b", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.10.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.10_2": { + "flake": false, + "locked": { + "lastModified": 1743069404, + "narHash": "sha256-q4kDFyJDDeoGqfEtrZRx4iqMVEC2MOzCToWsFY+TOzY=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "2318c61db3a01e03700bd4b05665662929b7fe8b", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.10.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.10_3": { + "flake": false, + "locked": { + "lastModified": 1743069404, + "narHash": "sha256-q4kDFyJDDeoGqfEtrZRx4iqMVEC2MOzCToWsFY+TOzY=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "2318c61db3a01e03700bd4b05665662929b7fe8b", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.10.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.11": { + "flake": false, + "locked": { + "lastModified": 1747306193, + "narHash": "sha256-/MmtpF8+FyQlwfKHqHK05BdsxC9LHV70d/FiMM7pzBM=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "46ef4523ea4949f47f6d2752476239f1c6d806fe", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.11.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.11_2": { + "flake": false, + "locked": { + "lastModified": 1747306193, + "narHash": "sha256-/MmtpF8+FyQlwfKHqHK05BdsxC9LHV70d/FiMM7pzBM=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "46ef4523ea4949f47f6d2752476239f1c6d806fe", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.11.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.11_3": { + "flake": false, + "locked": { + "lastModified": 1747306193, + "narHash": "sha256-/MmtpF8+FyQlwfKHqHK05BdsxC9LHV70d/FiMM7pzBM=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "46ef4523ea4949f47f6d2752476239f1c6d806fe", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.11.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.12": { + "flake": false, + "locked": { + "lastModified": 1758709460, + "narHash": "sha256-xkI8MIIVEVARskfWbGAgP5sHG/lyeKnkm0LIOJ19X5w=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "7d983de4fa7ff54369f6dd31444bdb9869aec83e", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.12.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.12_2": { + "flake": false, + "locked": { + "lastModified": 1758709460, + "narHash": "sha256-xkI8MIIVEVARskfWbGAgP5sHG/lyeKnkm0LIOJ19X5w=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "7d983de4fa7ff54369f6dd31444bdb9869aec83e", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.12.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.2": { + "flake": false, + "locked": { + "lastModified": 1693064058, + "narHash": "sha256-8DGIyz5GjuCFmohY6Fa79hHA/p1iIqubfJUTGQElbNk=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "b30f4b6cf5822f3112c35d14a0cba51f3fe23b85", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.2.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.2_2": { + "flake": false, + "locked": { + "lastModified": 1693064058, + "narHash": "sha256-8DGIyz5GjuCFmohY6Fa79hHA/p1iIqubfJUTGQElbNk=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "b30f4b6cf5822f3112c35d14a0cba51f3fe23b85", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.2.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.2_3": { + "flake": false, + "locked": { + "lastModified": 1693064058, + "narHash": "sha256-8DGIyz5GjuCFmohY6Fa79hHA/p1iIqubfJUTGQElbNk=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "b30f4b6cf5822f3112c35d14a0cba51f3fe23b85", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.2.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.3": { + "flake": false, + "locked": { + "lastModified": 1695910642, + "narHash": "sha256-tR58doOs3DncFehHwCLczJgntyG/zlsSd7DgDgMPOkI=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "458ccdb55c9ea22cd5d13ec3051aaefb295321be", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.3.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.3_2": { + "flake": false, + "locked": { + "lastModified": 1695910642, + "narHash": "sha256-tR58doOs3DncFehHwCLczJgntyG/zlsSd7DgDgMPOkI=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "458ccdb55c9ea22cd5d13ec3051aaefb295321be", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.3.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.3_3": { + "flake": false, + "locked": { + "lastModified": 1695910642, + "narHash": "sha256-tR58doOs3DncFehHwCLczJgntyG/zlsSd7DgDgMPOkI=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "458ccdb55c9ea22cd5d13ec3051aaefb295321be", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.3.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.4": { + "flake": false, + "locked": { + "lastModified": 1699862708, + "narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.4.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.4_2": { + "flake": false, + "locked": { + "lastModified": 1699862708, + "narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.4.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.4_3": { + "flake": false, + "locked": { + "lastModified": 1699862708, + "narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.4.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.5": { + "flake": false, + "locked": { + "lastModified": 1701080174, + "narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "27f8c3d3892e38edaef5bea3870161815c4d014c", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.5.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.5_2": { + "flake": false, + "locked": { + "lastModified": 1701080174, + "narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "27f8c3d3892e38edaef5bea3870161815c4d014c", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.5.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.5_3": { + "flake": false, + "locked": { + "lastModified": 1701080174, + "narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "27f8c3d3892e38edaef5bea3870161815c4d014c", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.5.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.6": { + "flake": false, + "locked": { + "lastModified": 1705325287, + "narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.6.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.6_2": { + "flake": false, + "locked": { + "lastModified": 1705325287, + "narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.6.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.6_3": { + "flake": false, + "locked": { + "lastModified": 1705325287, + "narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.6.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.7": { + "flake": false, + "locked": { + "lastModified": 1708965829, + "narHash": "sha256-LfJ+TBcBFq/XKoiNI7pc4VoHg4WmuzsFxYJ3Fu+Jf+M=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "50322b0a4aefb27adc5ec42f5055aaa8f8e38001", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.7.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.7_2": { + "flake": false, + "locked": { + "lastModified": 1708965829, + "narHash": "sha256-LfJ+TBcBFq/XKoiNI7pc4VoHg4WmuzsFxYJ3Fu+Jf+M=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "50322b0a4aefb27adc5ec42f5055aaa8f8e38001", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.7.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.7_3": { + "flake": false, + "locked": { + "lastModified": 1708965829, + "narHash": "sha256-LfJ+TBcBFq/XKoiNI7pc4VoHg4WmuzsFxYJ3Fu+Jf+M=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "50322b0a4aefb27adc5ec42f5055aaa8f8e38001", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.7.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.8": { + "flake": false, + "locked": { + "lastModified": 1715153580, + "narHash": "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "dd1be1beb16700de59e0d6801957290bcf956a0a", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.8.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.8_2": { + "flake": false, + "locked": { + "lastModified": 1715153580, + "narHash": "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "dd1be1beb16700de59e0d6801957290bcf956a0a", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.8.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.8_3": { + "flake": false, + "locked": { + "lastModified": 1715153580, + "narHash": "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "dd1be1beb16700de59e0d6801957290bcf956a0a", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.8.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.9": { + "flake": false, + "locked": { + "lastModified": 1719993701, + "narHash": "sha256-wy348++MiMm/xwtI9M3vVpqj2qfGgnDcZIGXw8sF1sA=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "90319a7e62ab93ab65a95f8f2bcf537e34dae76a", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.9.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.9_2": { + "flake": false, + "locked": { + "lastModified": 1719993701, + "narHash": "sha256-wy348++MiMm/xwtI9M3vVpqj2qfGgnDcZIGXw8sF1sA=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "90319a7e62ab93ab65a95f8f2bcf537e34dae76a", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.9.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.9_3": { + "flake": false, + "locked": { + "lastModified": 1719993701, + "narHash": "sha256-wy348++MiMm/xwtI9M3vVpqj2qfGgnDcZIGXw8sF1sA=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "90319a7e62ab93ab65a95f8f2bcf537e34dae76a", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.9.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls_2": { + "flake": false, + "locked": { + "lastModified": 1741604408, + "narHash": "sha256-tuq3+Ip70yu89GswZ7DSINBpwRprnWnl6xDYnS4GOsc=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "682d6894c94087da5e566771f25311c47e145359", + "type": "github" + }, + "original": { + "owner": "haskell", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls_3": { + "flake": false, + "locked": { + "lastModified": 1741604408, + "narHash": "sha256-tuq3+Ip70yu89GswZ7DSINBpwRprnWnl6xDYnS4GOsc=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "682d6894c94087da5e566771f25311c47e145359", + "type": "github" + }, + "original": { + "owner": "haskell", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hpc-coveralls": { + "flake": false, + "locked": { + "lastModified": 1607498076, + "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", + "type": "github" + }, + "original": { + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "type": "github" + } + }, + "hpc-coveralls_2": { + "flake": false, + "locked": { + "lastModified": 1607498076, + "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", + "type": "github" + }, + "original": { + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "type": "github" + } + }, + "hpc-coveralls_3": { + "flake": false, + "locked": { + "lastModified": 1607498076, + "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", "type": "github" }, "original": { @@ -492,10 +1719,49 @@ "type": "github" } }, + "incl": { + "inputs": { + "nixlib": "nixlib" + }, + "locked": { + "lastModified": 1693483555, + "narHash": "sha256-Beq4WhSeH3jRTZgC1XopTSU10yLpK1nmMcnGoXO0XYo=", + "owner": "divnix", + "repo": "incl", + "rev": "526751ad3d1e23b07944b14e3f6b7a5948d3007b", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "incl", + "type": "github" + } + }, + "incl_2": { + "inputs": { + "nixlib": "nixlib_2" + }, + "locked": { + "lastModified": 1693483555, + "narHash": "sha256-Beq4WhSeH3jRTZgC1XopTSU10yLpK1nmMcnGoXO0XYo=", + "owner": "divnix", + "repo": "incl", + "rev": "526751ad3d1e23b07944b14e3f6b7a5948d3007b", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "incl", + "type": "github" + } + }, "iohkNix": { "inputs": { "blst": "blst", - "nixpkgs": "nixpkgs", + "nixpkgs": [ + "cardano-node", + "nixpkgs" + ], "secp256k1": "secp256k1", "sodium": "sodium" }, @@ -513,7 +1779,86 @@ "type": "github" } }, + "iohkNix_2": { + "inputs": { + "blst": "blst_2", + "nixpkgs": [ + "cardano-node-tx-generator", + "nixpkgs" + ], + "secp256k1": "secp256k1_2", + "sodium": "sodium_2" + }, + "locked": { + "lastModified": 1777941182, + "narHash": "sha256-FX3+8GIrB2z4akmcYTStELDKVJWgqy9yFt0mxwpU3Qc=", + "owner": "input-output-hk", + "repo": "iohk-nix", + "rev": "9de00113c11ba8cac908a63acf34b193cda7475b", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-nix", + "type": "github" + } + }, + "iohkNix_3": { + "inputs": { + "blst": "blst_3", + "nixpkgs": "nixpkgs", + "secp256k1": "secp256k1_3", + "sodium": "sodium_3" + }, + "locked": { + "lastModified": 1777941182, + "narHash": "sha256-FX3+8GIrB2z4akmcYTStELDKVJWgqy9yFt0mxwpU3Qc=", + "owner": "input-output-hk", + "repo": "iohk-nix", + "rev": "9de00113c11ba8cac908a63acf34b193cda7475b", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "iohk-nix", + "type": "github" + } + }, "iserv-proxy": { + "flake": false, + "locked": { + "lastModified": 1755040634, + "narHash": "sha256-8W7uHpAIG8HhO3ig5OGHqvwduoye6q6dlrea1IrP2eI=", + "owner": "stable-haskell", + "repo": "iserv-proxy", + "rev": "1383d199a2c64f522979005d112b4fbdee38dd92", + "type": "github" + }, + "original": { + "owner": "stable-haskell", + "ref": "iserv-syms", + "repo": "iserv-proxy", + "type": "github" + } + }, + "iserv-proxy_2": { + "flake": false, + "locked": { + "lastModified": 1778457436, + "narHash": "sha256-bzZAHGzwcQGzBTipJuUs9tvMGO28kp0373zqnpn0g5A=", + "owner": "stable-haskell", + "repo": "iserv-proxy", + "rev": "8cdc446f8e2d91b120ecc075063e9475d387df52", + "type": "github" + }, + "original": { + "owner": "stable-haskell", + "ref": "iserv-syms", + "repo": "iserv-proxy", + "type": "github" + } + }, + "iserv-proxy_3": { "flake": false, "locked": { "lastModified": 1775620557, @@ -524,93 +1869,283 @@ "type": "github" }, "original": { - "owner": "stable-haskell", - "ref": "iserv-syms", - "repo": "iserv-proxy", + "owner": "stable-haskell", + "ref": "iserv-syms", + "repo": "iserv-proxy", + "type": "github" + } + }, + "nixlib": { + "locked": { + "lastModified": 1667696192, + "narHash": "sha256-hOdbIhnpWvtmVynKcsj10nxz9WROjZja+1wRAJ/C9+s=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "babd9cd2ca6e413372ed59fbb1ecc3c3a5fd3e5b", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixlib_2": { + "locked": { + "lastModified": 1667696192, + "narHash": "sha256-hOdbIhnpWvtmVynKcsj10nxz9WROjZja+1wRAJ/C9+s=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "babd9cd2ca6e413372ed59fbb1ecc3c3a5fd3e5b", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1751071626, + "narHash": "sha256-/uHE/AD2qGq4QLigWAnBHiVvpVXB04XAfrOtw8JMv+Y=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a47938d89bdf8e279ad432bd6a473cf4c430f48c", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "release-25.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2305": { + "locked": { + "lastModified": 1705033721, + "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2305_2": { + "locked": { + "lastModified": 1705033721, + "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2305_3": { + "locked": { + "lastModified": 1705033721, + "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2311": { + "locked": { + "lastModified": 1719957072, + "narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7144d6241f02d171d25fba3edeaf15e0f2592105", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2311_2": { + "locked": { + "lastModified": 1719957072, + "narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7144d6241f02d171d25fba3edeaf15e0f2592105", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2311_3": { + "locked": { + "lastModified": 1719957072, + "narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7144d6241f02d171d25fba3edeaf15e0f2592105", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2405": { + "locked": { + "lastModified": 1735564410, + "narHash": "sha256-HB/FA0+1gpSs8+/boEavrGJH+Eq08/R2wWNph1sM1Dg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1e7a8f391f1a490460760065fa0630b5520f9cf8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-24.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2405_2": { + "locked": { + "lastModified": 1735564410, + "narHash": "sha256-HB/FA0+1gpSs8+/boEavrGJH+Eq08/R2wWNph1sM1Dg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1e7a8f391f1a490460760065fa0630b5520f9cf8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-24.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2405_3": { + "locked": { + "lastModified": 1735564410, + "narHash": "sha256-HB/FA0+1gpSs8+/boEavrGJH+Eq08/R2wWNph1sM1Dg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1e7a8f391f1a490460760065fa0630b5520f9cf8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-24.05-darwin", + "repo": "nixpkgs", "type": "github" } }, - "nixpkgs": { + "nixpkgs-2411": { "locked": { - "lastModified": 1751071626, - "narHash": "sha256-/uHE/AD2qGq4QLigWAnBHiVvpVXB04XAfrOtw8JMv+Y=", - "owner": "nixos", + "lastModified": 1748037224, + "narHash": "sha256-92vihpZr6dwEMV6g98M5kHZIttrWahb9iRPBm1atcPk=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "a47938d89bdf8e279ad432bd6a473cf4c430f48c", + "rev": "f09dede81861f3a83f7f06641ead34f02f37597f", "type": "github" }, "original": { - "owner": "nixos", - "ref": "release-25.05", + "owner": "NixOS", + "ref": "nixpkgs-24.11-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2305": { + "nixpkgs-2411_2": { "locked": { - "lastModified": 1705033721, - "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", + "lastModified": 1751290243, + "narHash": "sha256-kNf+obkpJZWar7HZymXZbW+Rlk3HTEIMlpc6FCNz0Ds=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", + "rev": "5ab036a8d97cb9476fbe81b09076e6e91d15e1b6", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-23.05-darwin", + "ref": "nixpkgs-24.11-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2311": { + "nixpkgs-2411_3": { "locked": { - "lastModified": 1719957072, - "narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=", + "lastModified": 1751290243, + "narHash": "sha256-kNf+obkpJZWar7HZymXZbW+Rlk3HTEIMlpc6FCNz0Ds=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7144d6241f02d171d25fba3edeaf15e0f2592105", + "rev": "5ab036a8d97cb9476fbe81b09076e6e91d15e1b6", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-23.11-darwin", + "ref": "nixpkgs-24.11-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2405": { + "nixpkgs-2505": { "locked": { - "lastModified": 1735564410, - "narHash": "sha256-HB/FA0+1gpSs8+/boEavrGJH+Eq08/R2wWNph1sM1Dg=", + "lastModified": 1748852332, + "narHash": "sha256-r/wVJWmLYEqvrJKnL48r90Wn9HWX9SHFt6s4LhuTh7k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1e7a8f391f1a490460760065fa0630b5520f9cf8", + "rev": "a8167f3cc2f991dd4d0055746df53dae5fd0c953", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-24.05-darwin", + "ref": "nixpkgs-25.05-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2411": { + "nixpkgs-2505_2": { "locked": { - "lastModified": 1751290243, - "narHash": "sha256-kNf+obkpJZWar7HZymXZbW+Rlk3HTEIMlpc6FCNz0Ds=", + "lastModified": 1764560356, + "narHash": "sha256-M5aFEFPppI4UhdOxwdmceJ9bDJC4T6C6CzCK1E2FZyo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5ab036a8d97cb9476fbe81b09076e6e91d15e1b6", + "rev": "6c8f0cca84510cc79e09ea99a299c9bc17d03cb6", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-24.11-darwin", + "ref": "nixpkgs-25.05-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2505": { + "nixpkgs-2505_3": { "locked": { "lastModified": 1764560356, "narHash": "sha256-M5aFEFPppI4UhdOxwdmceJ9bDJC4T6C6CzCK1E2FZyo=", @@ -642,6 +2177,22 @@ "type": "github" } }, + "nixpkgs-2511_2": { + "locked": { + "lastModified": 1775749320, + "narHash": "sha256-msT6frWJSQ2WR+0cpk+KPcZdLTLagUIsJwQwIX9JNSo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "74b87959b2d16f59f54d8559cf3cf26b9d907949", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-25.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-lib": { "locked": { "lastModified": 1777168982, @@ -658,6 +2209,38 @@ } }, "nixpkgs-unstable": { + "locked": { + "lastModified": 1748856973, + "narHash": "sha256-RlTsJUvvr8ErjPBsiwrGbbHYW8XbB/oek0Gi78XdWKg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e4b09e47ace7d87de083786b404bf232eb6c89d8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-unstable_2": { + "locked": { + "lastModified": 1775888245, + "narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "13043924aaa7375ce482ebe2494338e058282925", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-unstable_3": { "locked": { "lastModified": 1775888245, "narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=", @@ -690,13 +2273,49 @@ "type": "github" } }, + "old-ghc-nix_2": { + "flake": false, + "locked": { + "lastModified": 1631092763, + "narHash": "sha256-sIKgO+z7tj4lw3u6oBZxqIhDrzSkvpHtv0Kki+lh9Fg=", + "owner": "angerman", + "repo": "old-ghc-nix", + "rev": "af48a7a7353e418119b6dfe3cd1463a657f342b8", + "type": "github" + }, + "original": { + "owner": "angerman", + "ref": "master", + "repo": "old-ghc-nix", + "type": "github" + } + }, + "old-ghc-nix_3": { + "flake": false, + "locked": { + "lastModified": 1631092763, + "narHash": "sha256-sIKgO+z7tj4lw3u6oBZxqIhDrzSkvpHtv0Kki+lh9Fg=", + "owner": "angerman", + "repo": "old-ghc-nix", + "rev": "af48a7a7353e418119b6dfe3cd1463a657f342b8", + "type": "github" + }, + "original": { + "owner": "angerman", + "ref": "master", + "repo": "old-ghc-nix", + "type": "github" + } + }, "root": { "inputs": { "CHaP": "CHaP", + "cardano-node": "cardano-node", + "cardano-node-tx-generator": "cardano-node-tx-generator", "flake-parts": "flake-parts", - "hackageNix": "hackageNix", - "haskellNix": "haskellNix", - "iohkNix": "iohkNix", + "hackageNix": "hackageNix_5", + "haskellNix": "haskellNix_3", + "iohkNix": "iohkNix_3", "nixpkgs": [ "haskellNix", "nixpkgs-unstable" @@ -720,6 +2339,40 @@ "type": "github" } }, + "secp256k1_2": { + "flake": false, + "locked": { + "lastModified": 1683999695, + "narHash": "sha256-9nJJVENMXjXEJZzw8DHzin1DkFkF8h9m/c6PuM7Uk4s=", + "owner": "bitcoin-core", + "repo": "secp256k1", + "rev": "acf5c55ae6a94e5ca847e07def40427547876101", + "type": "github" + }, + "original": { + "owner": "bitcoin-core", + "ref": "v0.3.2", + "repo": "secp256k1", + "type": "github" + } + }, + "secp256k1_3": { + "flake": false, + "locked": { + "lastModified": 1683999695, + "narHash": "sha256-9nJJVENMXjXEJZzw8DHzin1DkFkF8h9m/c6PuM7Uk4s=", + "owner": "bitcoin-core", + "repo": "secp256k1", + "rev": "acf5c55ae6a94e5ca847e07def40427547876101", + "type": "github" + }, + "original": { + "owner": "bitcoin-core", + "ref": "v0.3.2", + "repo": "secp256k1", + "type": "github" + } + }, "sodium": { "flake": false, "locked": { @@ -737,7 +2390,73 @@ "type": "github" } }, + "sodium_2": { + "flake": false, + "locked": { + "lastModified": 1675156279, + "narHash": "sha256-0uRcN5gvMwO7MCXVYnoqG/OmeBFi8qRVnDWJLnBb9+Y=", + "owner": "input-output-hk", + "repo": "libsodium", + "rev": "dbb48cce5429cb6585c9034f002568964f1ce567", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "libsodium", + "rev": "dbb48cce5429cb6585c9034f002568964f1ce567", + "type": "github" + } + }, + "sodium_3": { + "flake": false, + "locked": { + "lastModified": 1675156279, + "narHash": "sha256-0uRcN5gvMwO7MCXVYnoqG/OmeBFi8qRVnDWJLnBb9+Y=", + "owner": "input-output-hk", + "repo": "libsodium", + "rev": "dbb48cce5429cb6585c9034f002568964f1ce567", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "libsodium", + "rev": "dbb48cce5429cb6585c9034f002568964f1ce567", + "type": "github" + } + }, "stackage": { + "flake": false, + "locked": { + "lastModified": 1755648773, + "narHash": "sha256-NhcOu6GwYal+awBQLoMT4vf7L7Ar1DectDjK2mF653I=", + "owner": "input-output-hk", + "repo": "stackage.nix", + "rev": "1a0ea16d99761b93456460c255a8b723647b2c77", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "stackage.nix", + "type": "github" + } + }, + "stackage_2": { + "flake": false, + "locked": { + "lastModified": 1782175169, + "narHash": "sha256-x+fFnU8BP0ztG68OnvKNKpcwFbf8QxRh24dDfD7HCSc=", + "owner": "input-output-hk", + "repo": "stackage.nix", + "rev": "7234ccacc6e8938830b19792a64d2ce0771ebcbc", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "stackage.nix", + "type": "github" + } + }, + "stackage_3": { "flake": false, "locked": { "lastModified": 1778113854, @@ -752,6 +2471,72 @@ "repo": "stackage.nix", "type": "github" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 5dab645d41..c09048c3ad 100644 --- a/flake.nix +++ b/flake.nix @@ -14,6 +14,13 @@ inputs.nixpkgs.follows = "nixpkgs"; inputs.hackage.follows = "hackageNix"; }; + cardano-node.url = "github:IntersectMBO/cardano-node/11.0.1"; + # tx-generator only, for the integration suite's submission test: its + # Ogmios submission transport (IntersectMBO/cardano-node#6609) landed + # after 11.0.1. Separate from the input above so the node, cli and + # testnet binaries stay on the released tag the server is built against; + # fold it back into `cardano-node` once a release carries the transport. + cardano-node-tx-generator.url = "github:IntersectMBO/cardano-node/ec672fd082c7839792d0ec4a0bce27e96c9bb70f"; iohkNix.url = "github:input-output-hk/iohk-nix"; nixpkgs.follows = "haskellNix/nixpkgs-unstable"; self.submodules = true; @@ -23,11 +30,12 @@ inherit ((import ./flake/lib.nix {inherit inputs;}).flake.lib) recursiveImports; in inputs.flake-parts.lib.mkFlake {inherit inputs;} { - imports = recursiveImports [./perSystem]; + imports = recursiveImports [./perSystem] ++ [inputs.flake-parts.flakeModules.touchup]; + touchup.attr.formatter.enable = false; systems = [ "x86_64-linux" # "aarch64-linux" - # "aarch64-darwin" + "aarch64-darwin" ]; perSystem = {system, ...}: { _module.args.pkgs = import inputs.nixpkgs { diff --git a/perSystem/devShells.nix b/perSystem/devShells.nix index 88fcffa931..1c37d33c6f 100644 --- a/perSystem/devShells.nix +++ b/perSystem/devShells.nix @@ -1,11 +1,12 @@ { inputs, ... }: { - perSystem = { shellFor, pkgs, ... }: { + perSystem = { shellFor, hsPkgs, pkgs, system, ... }: { devShells.default = shellFor { packages = p: [ p.ogmios ]; nativeBuildInputs = [ pkgs.jq pkgs.gh + pkgs.lmdb ]; tools = { @@ -29,5 +30,30 @@ withHoogle = true; }; + + devShells.integration = let + cn = inputs.cardano-node.packages.${system}; + cnTxGenerator = inputs.cardano-node-tx-generator.packages.${system}; + in shellFor { + packages = p: [ p.ogmios p.ogmios-integration-tests ]; + + nativeBuildInputs = [ + pkgs.jq + pkgs.lmdb + hsPkgs.ogmios.components.exes.ogmios + cn.cardano-node + cn.cardano-cli + cn.cardano-testnet + cnTxGenerator.tx-generator + ]; + + tools = { + cabal = "latest"; + }; + + shellHook = '' + export LANG="en_US.UTF-8" + ''; + }; }; } diff --git a/perSystem/hydraJobs.nix b/perSystem/hydraJobs.nix index de9782dd9a..c53c1da734 100644 --- a/perSystem/hydraJobs.nix +++ b/perSystem/hydraJobs.nix @@ -3,11 +3,26 @@ { config, pkgs, + system, ... }: let - required = {inherit (config) packages checks;}; - nonRequired = {inherit (config) devShells;}; - jobs = required // nonRequired; + integrationChecks = [ "ogmios-integration" ]; + ciChecks = + if lib.hasSuffix "-linux" system + then config.checks + else removeAttrs config.checks integrationChecks; + required = { + inherit (config) packages; + checks = removeAttrs ciChecks integrationChecks; + }; + nonRequired = { + inherit (config) devShells; + checks = lib.filterAttrs (name: _: lib.elem name integrationChecks) ciChecks; + }; + jobs = { + inherit (config) packages devShells; + checks = ciChecks; + }; in jobs // { diff --git a/perSystem/musl.nix b/perSystem/musl.nix index 5af0824aa7..81a52f0d50 100644 --- a/perSystem/musl.nix +++ b/perSystem/musl.nix @@ -5,13 +5,19 @@ lib, ... }: let + # Only offer the static build from a Linux host. Cross-compiling to + # linux-musl from darwin means asking haskell.nix for a whole + # aarch64-unknown-linux-musl GHC that no cache has a substitute for, so it + # gets built from source and times out on the CI builders. + hostIsLinux = lib.hasSuffix "-linux" system; muslProject = project.projectCross.${ if system == "x86_64-linux" then "musl64" else "aarch64-multiplatform-musl" }; muslExes = muslProject.hsPkgs.ogmios.components.exes; - in { - packages.ogmios-musl = muslExes.ogmios; - }; + in + lib.optionalAttrs hostIsLinux { + packages.ogmios-musl = muslExes.ogmios; + }; } diff --git a/perSystem/packages.nix b/perSystem/packages.nix index ef27c9bb15..b1e686b32a 100644 --- a/perSystem/packages.nix +++ b/perSystem/packages.nix @@ -1,15 +1,81 @@ -{ - perSystem = { hsPkgs, ... }: +{ inputs, ... }: { + perSystem = { hsPkgs, pkgs, lib, system, ... }: let ogmios = hsPkgs.ogmios; + integrationTestsExe = + hsPkgs.ogmios-integration-tests.components.exes.ogmios-integration-tests; + cn = inputs.cardano-node.packages.${system}; + cnTxGenerator = inputs.cardano-node-tx-generator.packages.${system}; + + # The suite spawns ogmios, cardano-node, cardano-cli, cardano-testnet + # and tx-generator (Test.Integration.Env resolves them from PATH), so + # ship them as an execution dependency of the packaged binary: + # `nix run .#ogmios-integration-tests` is self-contained. Inside the + # `integration` devshell the same tools come from the shell's PATH + # instead, and `cabal run` builds are intentionally left unwrapped so + # they can be pointed at other tool versions. + integrationTests = + pkgs.runCommand "ogmios-integration-tests" + { + nativeBuildInputs = [ pkgs.makeWrapper ]; + meta.mainProgram = "ogmios-integration-tests"; + } + '' + makeWrapper \ + ${integrationTestsExe}/bin/ogmios-integration-tests \ + $out/bin/ogmios-integration-tests \ + --prefix PATH : ${lib.makeBinPath [ + ogmios.components.exes.ogmios + cn.cardano-node + cn.cardano-cli + cn.cardano-testnet + cnTxGenerator.tx-generator + ]} + ''; in { packages = { default = ogmios.components.exes.ogmios; ogmios = ogmios.components.exes.ogmios; ogmios-lib = ogmios.components.library; + ogmios-integration-tests = integrationTests; }; - checks.ogmios-unit = ogmios.checks.unit; + checks = { + ogmios-unit = ogmios.checks.unit; + + # Actually executes the integration suite: a throwaway testnet, + # ogmios and tx-generator all run inside the build, talking over + # loopback. Defined for every system so it can always be run by + # hand (`nix build .#checks..ogmios-integration`, or + # `nix flake check`); hydra however only schedules it on Linux, + # where the sandbox's network namespace keeps the suite's fixed + # ogmios port private to the build - see hydraJobs.nix. + ogmios-integration = + pkgs.runCommand "ogmios-integration" + { + nativeBuildInputs = [ integrationTests ]; + # Sandboxed darwin builds block networking by default; the + # suite only ever talks to 127.0.0.1. + __darwinAllowLocalNetworking = true; + meta = { + description = "run the ogmios integration suite against a local testnet"; + # ~2 minutes when healthy; don't let a wedged daemon hold a + # builder for hours. + timeout = 1800; + }; + } + '' + ogmios-integration-tests || { + echo "==== integration suite failed; daemon log tails follow" + for f in "$TMPDIR"/ogmios-integration-*/logs/*; do + echo "==== $f" + tail -n 40 "$f" || true + done + exit 1 + } + touch $out + ''; + }; }; } diff --git a/perSystem/project.nix b/perSystem/project.nix index 0221d777d7..cbcbefd506 100644 --- a/perSystem/project.nix +++ b/perSystem/project.nix @@ -48,14 +48,10 @@ cp ${self}/server/ogmios.json . cp ${self}/server/cardano.json . - # Test.Path.Util.getProjectRoot uses TH `makeRelativeToProject ""` - # which bakes the compile-time absolute path - # /build/ogmios-src-test-unit-root/server into the binary. Recreate - # that path at runtime with the golden fixtures and the - # cardano-configurations submodule content. - mkdir -p /build/ogmios-src-test-unit-root/server/test - cp -r ${self}/server/test/golden /build/ogmios-src-test-unit-root/server/test/golden - cp -r ${self}/server/config /build/ogmios-src-test-unit-root/server/config + export OGMIOS_TEST_PROJECT_ROOT="$TMPDIR/ogmios-project-root" + mkdir -p "$OGMIOS_TEST_PROJECT_ROOT/test" + cp -r ${self}/server/test/golden "$OGMIOS_TEST_PROJECT_ROOT/test/golden" + cp -r ${self}/server/config "$OGMIOS_TEST_PROJECT_ROOT/config" ''; } ({pkgs, ...}: { diff --git a/server/cabal.project b/server/cabal.project index 66168b150f..3581429cdb 100644 --- a/server/cabal.project +++ b/server/cabal.project @@ -24,7 +24,7 @@ constraints: , any.io-classes ^>= 1.8 , any.formatting == 7.2.0 , any.validation < 1.2 - , any.text source + , any.text >= 2.1.2 -- ogmios doesn't exercise io_uring code paths, so build `blockio` with the -- serial backend to drop the `liburing` system dependency. @@ -46,6 +46,7 @@ packages: modules/hjsonschema modules/hspec-json-schema modules/json-rpc + test/integration tests: False diff --git a/server/src/Ogmios/Data/Json/Byron.hs b/server/src/Ogmios/Data/Json/Byron.hs index 219372c0ff..7d0c8703df 100644 --- a/server/src/Ogmios/Data/Json/Byron.hs +++ b/server/src/Ogmios/Data/Json/Byron.hs @@ -44,6 +44,7 @@ import qualified Cardano.Crypto.Signing as By import qualified Cardano.Crypto.Wallet as CC import qualified Cardano.Ledger.Binary as Binary import qualified Cardano.Ledger.Core as Ledger +import qualified Data.ByteString as BS encodeAddress :: By.Address @@ -85,11 +86,11 @@ encodeABlockOrBoundary opts = encodeObject . \case ) <> "issuer" .= encodeObject - ( "verificationKey" .= encodeVerificationKey (By.Dlg.issuerVK c) + ( "verificationKey" .= encodeExtendedVerificationKey (By.Dlg.issuerVK c) ) <> "delegate" .= encodeObject - ( "verificationKey" .= encodeVerificationKey (By.Dlg.delegateVK c) + ( "verificationKey" .= encodeExtendedVerificationKey (By.Dlg.delegateVK c) ) where h = By.blockHeader blk @@ -126,11 +127,11 @@ encodeACertificate encodeACertificate x = encodeObject ( "issuer" .= encodeObject - ( "verificationKey" .= encodeVerificationKey (By.Dlg.issuerVK x) + ( "verificationKey" .= encodeExtendedVerificationKey (By.Dlg.issuerVK x) ) <> "delegate" .= encodeObject - ( "verificationKey" .= encodeVerificationKey (By.Dlg.delegateVK x) + ( "verificationKey" .= encodeExtendedVerificationKey (By.Dlg.delegateVK x) ) ) @@ -302,7 +303,7 @@ encodeAVote x = encodeObject ) <> "voter" .= encodeObject ( "verificationKey" .= - encodeVerificationKey (By.Upd.Vote.voterVK x) + encodeExtendedVerificationKey (By.Upd.Vote.voterVK x) ) ) @@ -535,9 +536,9 @@ encodeTxInWitness -> Json encodeTxInWitness = encodeObject . \case By.VKWitness key sig -> - "key" .= - encodeVerificationKey key <> - "signature" .= + mempty + <> encodeVerificationKey key + <> "signature" .= encodeSignature sig By.RedeemWitness key sig -> "key" .= @@ -557,12 +558,22 @@ encodeTxProof x = encodeHash (By.txpWitnessesHash x) & encodeObject -encodeVerificationKey +encodeExtendedVerificationKey :: By.VerificationKey -> Json -encodeVerificationKey = +encodeExtendedVerificationKey = encodeByteStringBase16 . CC.unXPub . By.unVerificationKey +encodeVerificationKey + :: By.VerificationKey + -> Series +encodeVerificationKey x = + "key" .= encodeByteStringBase16 (BS.take 32 xpub) + <> + "chainCode" .= encodeByteStringBase16 (BS.drop 32 xpub) + where + xpub = CC.unXPub (By.unVerificationKey x) + stringifyAddress :: By.Address -> Text diff --git a/server/test/integration/ogmios-integration-tests.cabal b/server/test/integration/ogmios-integration-tests.cabal new file mode 100644 index 0000000000..484a6f78a9 --- /dev/null +++ b/server/test/integration/ogmios-integration-tests.cabal @@ -0,0 +1,41 @@ +cabal-version: 3.0 +name: ogmios-integration-tests +version: 0.1.0.0 +license: MPL-2.0 +build-type: Simple + +executable ogmios-integration-tests + main-is: Main.hs + hs-source-dirs: src + default-language: Haskell2010 + ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N + + other-modules: + Test.Integration.Constitution + Test.Integration.DelegateRepresentatives + Test.Integration.Env + Test.Integration.Epoch + Test.Integration.LedgerTip + Test.Integration.LiveStakeDistribution + Test.Integration.NetworkTip + Test.Integration.ProtocolParameters + Test.Integration.Query + Test.Integration.RewardAccountSummaries + Test.Integration.StakePools + Test.Integration.TxSubmission + Test.Integration.Utxo + + build-depends: + , base >= 4.14 && < 5 + , aeson + , bytestring + , containers + , directory + , filepath + , network + , process + , tasty >= 1.5.4 + , tasty-hunit + , temporary + , text + , websockets diff --git a/server/test/integration/src/Main.hs b/server/test/integration/src/Main.hs new file mode 100644 index 0000000000..9b195e9fc4 --- /dev/null +++ b/server/test/integration/src/Main.hs @@ -0,0 +1,38 @@ +module Main (main) where + +import Test.Tasty (DependencyType(..), defaultMain, dependentTestGroup, testGroup) +import Test.Integration.Constitution (constitutionTests) +import Test.Integration.DelegateRepresentatives (delegateRepresentativesTests) +import Test.Integration.Env (withTestEnv) +import Test.Integration.Epoch (epochTests) +import Test.Integration.LedgerTip (ledgerTipTests) +import Test.Integration.LiveStakeDistribution (liveStakeDistributionTests) +import Test.Integration.NetworkTip (networkTipTests) +import Test.Integration.ProtocolParameters (protocolParametersTests) +import Test.Integration.RewardAccountSummaries (rewardAccountSummariesTests) +import Test.Integration.StakePools (stakePoolsTests) +import Test.Integration.TxSubmission (txSubmissionTests) +import Test.Integration.Utxo (utxoTests) + +-- The query tests compare ogmios against cardano-cli on a ledger nothing is +-- moving underneath them, so submitting transactions has to wait until they +-- have all finished — 'AllFinish' rather than 'AllSucceed', since a failing +-- comparison is no reason to skip submission coverage. +main :: IO () +main = defaultMain $ + withTestEnv $ \getEnv -> + dependentTestGroup "Ogmios Integration Tests" AllFinish + [ testGroup "Queries" + [ utxoTests getEnv + , protocolParametersTests getEnv + , ledgerTipTests getEnv + , networkTipTests getEnv + , epochTests getEnv + , stakePoolsTests getEnv + , rewardAccountSummariesTests getEnv + , liveStakeDistributionTests getEnv + , constitutionTests getEnv + , delegateRepresentativesTests getEnv + ] + , txSubmissionTests getEnv + ] diff --git a/server/test/integration/src/Test/Integration/Constitution.hs b/server/test/integration/src/Test/Integration/Constitution.hs new file mode 100644 index 0000000000..837152c743 --- /dev/null +++ b/server/test/integration/src/Test/Integration/Constitution.hs @@ -0,0 +1,43 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.Constitution + ( constitutionTests + ) where + +import Data.Aeson (Value, (.:)) +import Data.Aeson.Types (Parser, withObject) +import Data.Text (Text) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertEqual, testCase) + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (parseIO, queryCli, queryOgmios) + +constitutionTests :: IO TestEnv -> TestTree +constitutionTests getEnv = testGroup "Constitution" + [ testCase "constitution matches cardano-cli" $ do + env <- getEnv + + oHash <- parseIO "ogmios constitution" parseOgmiosHash + =<< queryOgmios env "queryLedgerState/constitution" Nothing + + cHash <- parseIO "cli constitution" parseCliHash + =<< queryCli env ["conway", "query", "constitution"] + + assertEqual "constitution anchor hash" oHash cHash + ] + +-- --------------------------------------------------------------------------- +-- Parsers +-- --------------------------------------------------------------------------- + +parseOgmiosHash :: Value -> Parser Text +parseOgmiosHash = withObject "constitution" $ \o -> do + metadata <- o .: "metadata" + metadata .: "hash" + +parseCliHash :: Value -> Parser Text +parseCliHash = withObject "constitution" $ \o -> do + anchor <- o .: "anchor" + anchor .: "dataHash" diff --git a/server/test/integration/src/Test/Integration/DelegateRepresentatives.hs b/server/test/integration/src/Test/Integration/DelegateRepresentatives.hs new file mode 100644 index 0000000000..ecf2f987ff --- /dev/null +++ b/server/test/integration/src/Test/Integration/DelegateRepresentatives.hs @@ -0,0 +1,66 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.DelegateRepresentatives + ( delegateRepresentativesTests + ) where + +import Data.Aeson (Value(..)) +import Data.Aeson.Types (Parser, withArray, withObject) +import Data.Foldable (toList) +import Data.Set (Set) +import Data.Text (Text) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (testCase) + +import qualified Data.Aeson.KeyMap as KM +import qualified Data.Set as Set + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (assertSameSet, parseIO, queryCli, queryOgmios) + +delegateRepresentativesTests :: IO TestEnv -> TestTree +delegateRepresentativesTests getEnv = testGroup "DelegateRepresentatives" + [ testCase "DRep IDs match cardano-cli" $ do + env <- getEnv + + ogmiosDreps <- parseIO "ogmios DRep IDs" parseOgmiosDrepIds + =<< queryOgmios env "queryLedgerState/delegateRepresentatives" Nothing + + cliDreps <- parseIO "cli DRep IDs" parseCliDrepIds + =<< queryCli env ["conway", "query", "drep-state", "--all-dreps"] + + assertSameSet "DRep ID sets" ogmiosDreps cliDreps + ] + +-- --------------------------------------------------------------------------- +-- Parsers +-- --------------------------------------------------------------------------- + +-- Ogmios returns an array of objects with "id" and "type" fields. +-- Special entries "abstain" and "noConfidence" have no "id". +parseOgmiosDrepIds :: Value -> Parser (Set Text) +parseOgmiosDrepIds = withArray "dreps" $ \arr -> do + ids <- mapM extractId (toList arr) + pure $ Set.fromList [i | Just i <- ids] + where + extractId = withObject "drep" $ \o -> + case KM.lookup "id" o of + Just (String drepId) -> pure (Just drepId) + _ -> pure Nothing + +-- cardano-cli drep-state --all-dreps returns an array of [drepId, drepState] pairs +parseCliDrepIds :: Value -> Parser (Set Text) +parseCliDrepIds = withArray "dreps" $ \arr -> do + ids <- mapM extractId (toList arr) + pure (Set.fromList ids) + where + extractId = withArray "pair" $ \pair -> case toList pair of + (drepId:_) -> case drepId of + Object o -> case KM.lookup "keyHash" o of + Just (String h) -> pure h + _ -> case KM.lookup "scriptHash" o of + Just (String h) -> pure h + _ -> fail $ "No keyHash or scriptHash in DRep ID: " <> show o + _ -> fail $ "Expected object for DRep ID, got: " <> show drepId + _ -> fail "Empty DRep pair" diff --git a/server/test/integration/src/Test/Integration/Env.hs b/server/test/integration/src/Test/Integration/Env.hs new file mode 100644 index 0000000000..47f2df3760 --- /dev/null +++ b/server/test/integration/src/Test/Integration/Env.hs @@ -0,0 +1,304 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + +module Test.Integration.Env + ( TestEnv(..) + , withTestEnv + , queryOgmiosRetry + ) where + +import Control.Concurrent (threadDelay) +import Control.Exception (SomeException, bracket, bracketOnError, try) +import Control.Monad (filterM, void) +import Data.Foldable (toList) +import Data.IORef (IORef, modifyIORef', newIORef, readIORef) +import Data.Maybe (catMaybes, listToMaybe) +import Network.Socket + ( AddrInfo(..) + , SocketType(..) + , close + , connect + , defaultHints + , getAddrInfo + , openSocket + ) +import System.Directory + ( createDirectoryIfMissing + , doesDirectoryExist + , doesFileExist + , findExecutable + , listDirectory + ) +import System.Environment (getEnvironment) +import System.FilePath ((), takeFileName) +import System.IO (Handle, IOMode(..), hClose, openFile, withFile) +import System.IO.Temp (createTempDirectory, getCanonicalTemporaryDirectory) +import System.Exit (ExitCode(..)) +import System.Process + ( CreateProcess(..) + , ProcessHandle + , StdStream(..) + , createProcess + , proc + , terminateProcess + , waitForProcess + , withCreateProcess + ) +import Test.Tasty (TestTree, withResource) + +data TestEnv = TestEnv + { envWorkDir :: !FilePath + , envTestnetDir :: !FilePath + , envNodeSocket :: !FilePath + , envNodeConfig :: !FilePath + , envOgmiosPort :: !Int + , envTestnetMagic :: !Int + } + +data ManagedState = ManagedState + { msProcesses :: [ProcessHandle] + , msLogHandles :: [Handle] + , msWorkDir :: FilePath + } + +ogmiosPort :: Int +ogmiosPort = 11337 + +testnetMagic :: Int +testnetMagic = 42 + +withTestEnv :: (IO TestEnv -> TestTree) -> TestTree +withTestEnv f = withResource setup' teardown' (f . fmap fst) + where + setup' = setup + teardown' (_, managed) = teardown managed + +-- withResource only runs teardown when setup returns, so everything +-- spawned here is also registered in a cleanup stack that bracketOnError +-- unwinds (newest first) if a later step of setup throws; once setup +-- returns, teardown owns cleanup. +setup :: IO (TestEnv, ManagedState) +setup = bracketOnError (newIORef []) rollback $ \cleanups -> do + let track action = modifyIORef' cleanups (action :) + + tmpBase <- getCanonicalTemporaryDirectory + workDir <- createTempDirectory tmpBase "ogmios-integration" + let logsDir = workDir "logs" + testnetDir = workDir "testnet" + createDirectoryIfMissing True logsDir + + putStrLn $ "[integration] work directory: " <> workDir + + -- Resolve tool paths for cardano-testnet's env vars + toolEnv <- resolveToolEnv + + -- Start cardano-testnet + testnetStdout <- openFile (logsDir "cardano-testnet.stdout") WriteMode + track (hClose testnetStdout) + testnetStderr <- openFile (logsDir "cardano-testnet.stderr") WriteMode + track (hClose testnetStderr) + (_, _, _, testnetPh) <- createProcess + (proc "cardano-testnet" + [ "cardano" + , "--testnet-magic", show testnetMagic + , "--output-dir", testnetDir + ]) + { env = Just toolEnv + , std_out = UseHandle testnetStdout + , std_err = UseHandle testnetStderr + } + track (terminateProcess testnetPh >> void (waitForProcess testnetPh)) + + putStrLn "[integration] waiting for cardano-testnet node socket..." + socketPath <- waitForFile testnetDir ["sock"] 120 + putStrLn $ "[integration] found node socket: " <> socketPath + + configPath <- findNodeConfig testnetDir + putStrLn $ "[integration] found node config: " <> configPath + + -- Start ogmios + ogmiosStdout <- openFile (logsDir "ogmios.stdout") WriteMode + track (hClose ogmiosStdout) + ogmiosStderr <- openFile (logsDir "ogmios.stderr") WriteMode + track (hClose ogmiosStderr) + (_, _, _, ogmiosPh) <- createProcess + (proc "ogmios" + [ "--node-socket", socketPath + , "--node-config", configPath + , "--port", show ogmiosPort + , "--log-level", "error" + ]) + { std_out = UseHandle ogmiosStdout + , std_err = UseHandle ogmiosStderr + } + track (terminateProcess ogmiosPh >> void (waitForProcess ogmiosPh)) + + putStrLn "[integration] waiting for ogmios..." + waitForTcpPort ogmiosPort 60 + putStrLn "[integration] ogmios is ready." + + -- Run tx-generator to populate the UTxO set + runTxGenerator workDir logsDir testnetDir + + let testEnv = TestEnv + { envWorkDir = workDir + , envTestnetDir = testnetDir + , envNodeSocket = socketPath + , envNodeConfig = configPath + , envOgmiosPort = ogmiosPort + , envTestnetMagic = testnetMagic + } + managed = ManagedState + { msProcesses = [ogmiosPh, testnetPh] + , msLogHandles = [testnetStdout, testnetStderr, ogmiosStdout, ogmiosStderr] + , msWorkDir = workDir + } + pure (testEnv, managed) + where + rollback :: IORef [IO ()] -> IO () + rollback cleanups = readIORef cleanups >>= sequence_ + +teardown :: ManagedState -> IO () +teardown ms = do + mapM_ (\ph -> terminateProcess ph >> waitForProcess ph) (msProcesses ms) + mapM_ hClose (msLogHandles ms) + putStrLn $ "[integration] logs available at: " <> msWorkDir ms "logs" + +-- --------------------------------------------------------------------------- +-- Helpers +-- --------------------------------------------------------------------------- + +-- cardano-testnet needs CARDANO_CLI and CARDANO_NODE env vars +-- pointing at the executables (it won't find them via PATH alone). +resolveToolEnv :: IO [(String, String)] +resolveToolEnv = do + cliPath <- requireExe "cardano-cli" + nodePath <- requireExe "cardano-node" + baseEnv <- getEnvironment + pure $ baseEnv + <> [ ("CARDANO_CLI", cliPath) + , ("CARDANO_NODE", nodePath) + ] + where + requireExe name = do + mPath <- findExecutable name + case mPath of + Just p -> pure p + Nothing -> fail $ name <> " not found on PATH" + +waitForFile :: FilePath -> [String] -> Int -> IO FilePath +waitForFile dir names maxSeconds = go maxSeconds + where + go 0 = fail $ + "File(s) " <> show names <> " not found under " <> dir + <> " after " <> show maxSeconds <> "s" + go n = do + result <- tryNames names + case result of + Just path -> pure path + Nothing -> threadDelay 1000000 >> go (n - 1) + + tryNames [] = pure Nothing + tryNames (name:rest) = do + result <- findRecursive dir name + case result of + Just path -> pure (Just path) + Nothing -> tryNames rest + +findRecursive :: FilePath -> String -> IO (Maybe FilePath) +findRecursive dir name = do + exists <- doesDirectoryExist dir + if not exists + then pure Nothing + else do + entries <- listDirectory dir + let fullPaths = map (dir ) entries + files <- filterM doesFileExist fullPaths + case filter (\p -> takeFileName p == name) files of + (f:_) -> pure (Just f) + [] -> do + dirs <- filterM doesDirectoryExist fullPaths + results <- mapM (\d -> findRecursive d name) dirs + pure . listToMaybe . catMaybes $ toList results + +findNodeConfig :: FilePath -> IO FilePath +findNodeConfig dir = tryNames configNames + where + configNames = ["configuration.yaml", "configuration.json", "config.json"] + + tryNames [] = fail $ + "No node configuration found under " <> dir + <> " (tried: " <> show configNames <> ")" + tryNames (name:rest) = do + result <- findRecursive dir name + case result of + Just path -> pure path + Nothing -> tryNames rest + +runTxGenerator :: FilePath -> FilePath -> FilePath -> IO () +runTxGenerator workDir logsDir testnetDir = do + let configFile = workDir "tx-generator-config.json" + sigKeyPath = testnetDir "utxo-keys" "utxo1" "utxo.skey" + writeFile configFile $ unlines + [ "{" + , " \"tx_count\": 30," + , " \"tps\": 10," + , " \"inputs_per_tx\": 2," + , " \"outputs_per_tx\": 2," + , " \"tx_fee\": 212345," + , " \"min_utxo_value\": 1000000," + , " \"add_tx_size\": 39," + , " \"init_cooldown\": 5," + , " \"era\": \"Conway\"," + , " \"keepalive\": 30," + , " \"debugMode\": false," + , " \"plutus\": null," + , " \"sigKey\": " <> show sigKeyPath + , "}" + ] + putStrLn "[integration] running tx-generator..." + exitCode <- + withFile (logsDir "tx-generator.stdout") WriteMode $ \txgenStdout -> + withFile (logsDir "tx-generator.stderr") WriteMode $ \txgenStderr -> + withCreateProcess + (proc "tx-generator" + [ "json_highlevel", configFile + , "--testnet-config-dir", testnetDir + ]) + { cwd = Just workDir + , std_out = UseHandle txgenStdout + , std_err = UseHandle txgenStderr + } + $ \_ _ _ ph -> waitForProcess ph + case exitCode of + ExitSuccess -> putStrLn "[integration] tx-generator finished." + ExitFailure c -> fail $ "tx-generator exited with code " <> show c + +waitForTcpPort :: Int -> Int -> IO () +waitForTcpPort port maxSeconds = go maxSeconds + where + go 0 = fail $ + "Port " <> show port <> " not accepting connections after " + <> show maxSeconds <> "s" + go n = do + result <- try @SomeException $ do + let hints = defaultHints { addrSocketType = Stream } + addr:_ <- getAddrInfo (Just hints) (Just "127.0.0.1") (Just (show port)) + bracket (openSocket addr) close (\sock -> connect sock (addrAddress addr)) + case result of + Right () -> pure () + Left _ -> threadDelay 1000000 >> go (n - 1) + +-- | Query ogmios via WebSocket with retries. +-- Ogmios may temporarily lose its node connection (especially after +-- tx-generator load), so we retry on any exception. +queryOgmiosRetry :: Int -> (Int -> IO a) -> IO a +queryOgmiosRetry port action = go (5 :: Int) + where + go 0 = action port + go n = do + result <- try @SomeException (action port) + case result of + Right val -> pure val + Left _ -> threadDelay 2000000 >> go (n - 1) diff --git a/server/test/integration/src/Test/Integration/Epoch.hs b/server/test/integration/src/Test/Integration/Epoch.hs new file mode 100644 index 0000000000..91a8afe0d4 --- /dev/null +++ b/server/test/integration/src/Test/Integration/Epoch.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.Epoch + ( epochTests + ) where + +import Data.Aeson ((.:), parseJSON) +import Data.Aeson.Types (withObject) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertEqual, testCase) + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (parseIO, queryCli, queryOgmios) + +epochTests :: IO TestEnv -> TestTree +epochTests getEnv = testGroup "Epoch" + [ testCase "epoch matches cardano-cli tip epoch" $ do + env <- getEnv + + ogmiosEpoch <- parseIO "ogmios epoch" parseJSON + =<< queryOgmios env "queryLedgerState/epoch" Nothing + + cliEpoch <- parseIO "cli tip epoch" (withObject "tip" (.: "epoch")) + =<< queryCli env ["conway", "query", "tip"] + + assertEqual "epoch" (ogmiosEpoch :: Integer) cliEpoch + ] diff --git a/server/test/integration/src/Test/Integration/LedgerTip.hs b/server/test/integration/src/Test/Integration/LedgerTip.hs new file mode 100644 index 0000000000..f3f25960cf --- /dev/null +++ b/server/test/integration/src/Test/Integration/LedgerTip.hs @@ -0,0 +1,42 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.LedgerTip + ( ledgerTipTests + ) where + +import Data.Aeson (Value, (.:)) +import Data.Aeson.Key (Key) +import Data.Aeson.Types (withObject) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertBool, testCase) + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (parseIO, queryCli, queryOgmios) + +ledgerTipTests :: IO TestEnv -> TestTree +ledgerTipTests getEnv = testGroup "LedgerTip" + [ testCase "ledgerState/tip slot is bracketed by cardano-cli tip" $ do + env <- getEnv + + cliBefore <- queryCli env ["conway", "query", "tip"] + cSlotBefore <- parseField cliBefore "slot" + + ogmiosTip <- queryOgmios env "queryLedgerState/tip" Nothing + oSlot <- parseField ogmiosTip "slot" + + cliAfter <- queryCli env ["conway", "query", "tip"] + cSlotAfter <- parseField cliAfter "slot" + + assertBool + ("Expected cli_before <= ogmios <= cli_after, got: " + <> show cSlotBefore <> " <= " <> show oSlot <> " <= " <> show cSlotAfter) + (cSlotBefore <= oSlot && oSlot <= cSlotAfter) + ] + +-- --------------------------------------------------------------------------- +-- Helpers +-- --------------------------------------------------------------------------- + +parseField :: Value -> Key -> IO Integer +parseField val key = parseIO ("field " <> show key) (withObject "obj" (.: key)) val diff --git a/server/test/integration/src/Test/Integration/LiveStakeDistribution.hs b/server/test/integration/src/Test/Integration/LiveStakeDistribution.hs new file mode 100644 index 0000000000..3cf1fbfe31 --- /dev/null +++ b/server/test/integration/src/Test/Integration/LiveStakeDistribution.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.LiveStakeDistribution + ( liveStakeDistributionTests + ) where + +import Data.Aeson (Value) +import Data.Aeson.Types (Parser, withObject) +import Data.Set (Set) +import Data.Text (Text) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (testCase) + +import qualified Data.Aeson.Key as Key +import qualified Data.Aeson.KeyMap as KM +import qualified Data.Set as Set + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (assertSameSet, parseIO, queryCliStdout, queryOgmios) + +liveStakeDistributionTests :: IO TestEnv -> TestTree +liveStakeDistributionTests getEnv = testGroup "LiveStakeDistribution" + [ testCase "pool IDs in stake-distribution match" $ do + env <- getEnv + + ogmiosPoolIds <- parseIO "ogmios pools" parsePoolIds + =<< queryOgmios env "queryLedgerState/liveStakeDistribution" Nothing + + -- cardano-cli stake-distribution has no --out-file and prints a text + -- table by default, so take JSON from stdout instead + cliPoolIds <- parseIO "cli pools" parsePoolIds + =<< queryCliStdout env ["conway", "query", "stake-distribution", "--output-json"] + + assertSameSet "Stake distribution pool ID sets" ogmiosPoolIds cliPoolIds + ] + +-- --------------------------------------------------------------------------- +-- Parsers +-- --------------------------------------------------------------------------- + +-- Both sides return an object keyed by pool ID +parsePoolIds :: Value -> Parser (Set Text) +parsePoolIds = withObject "distribution" $ \o -> + pure $ Set.fromList $ map Key.toText (KM.keys o) diff --git a/server/test/integration/src/Test/Integration/NetworkTip.hs b/server/test/integration/src/Test/Integration/NetworkTip.hs new file mode 100644 index 0000000000..c780231471 --- /dev/null +++ b/server/test/integration/src/Test/Integration/NetworkTip.hs @@ -0,0 +1,42 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.NetworkTip + ( networkTipTests + ) where + +import Data.Aeson (Value, (.:)) +import Data.Aeson.Key (Key) +import Data.Aeson.Types (withObject) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertBool, testCase) + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (parseIO, queryCli, queryOgmios) + +networkTipTests :: IO TestEnv -> TestTree +networkTipTests getEnv = testGroup "NetworkTip" + [ testCase "network/tip slot is bracketed by cardano-cli tip" $ do + env <- getEnv + + cliBefore <- queryCli env ["conway", "query", "tip"] + cSlotBefore <- parseField cliBefore "slot" + + ogmiosTip <- queryOgmios env "queryNetwork/tip" Nothing + oSlot <- parseField ogmiosTip "slot" + + cliAfter <- queryCli env ["conway", "query", "tip"] + cSlotAfter <- parseField cliAfter "slot" + + assertBool + ("Expected cli_before <= ogmios <= cli_after, got: " + <> show cSlotBefore <> " <= " <> show oSlot <> " <= " <> show cSlotAfter) + (cSlotBefore <= oSlot && oSlot <= cSlotAfter) + ] + +-- --------------------------------------------------------------------------- +-- Helpers +-- --------------------------------------------------------------------------- + +parseField :: Value -> Key -> IO Integer +parseField val key = parseIO ("field " <> show key) (withObject "obj" (.: key)) val diff --git a/server/test/integration/src/Test/Integration/ProtocolParameters.hs b/server/test/integration/src/Test/Integration/ProtocolParameters.hs new file mode 100644 index 0000000000..8962a4ec14 --- /dev/null +++ b/server/test/integration/src/Test/Integration/ProtocolParameters.hs @@ -0,0 +1,59 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.ProtocolParameters + ( protocolParametersTests + ) where + +import Control.Monad (foldM) +import Data.Aeson (Value, (.:)) +import Data.Aeson.Key (Key) +import Data.Aeson.Types (withObject) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertEqual, testCase) + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (parseIO, queryCli, queryOgmios) + +protocolParametersTests :: IO TestEnv -> TestTree +protocolParametersTests getEnv = testGroup "ProtocolParameters" + [ testCase "protocolParameters match cardano-cli" $ do + env <- getEnv + + ogmiosResult <- queryOgmios env "queryLedgerState/protocolParameters" Nothing + cliResult <- queryCli env ["conway", "query", "protocol-parameters"] + + oMinFeeCoeff <- ogmiosResult `getField` ["minFeeCoefficient"] + cMinFeeCoeff <- cliResult `getField` ["txFeePerByte"] + assertEqual "minFeeCoefficient" oMinFeeCoeff cMinFeeCoeff + + oMinFeeConst <- ogmiosResult `getField` ["minFeeConstant", "ada", "lovelace"] + cMinFeeConst <- cliResult `getField` ["txFeeFixed"] + assertEqual "minFeeConstant" oMinFeeConst cMinFeeConst + + oMaxBlockBody <- ogmiosResult `getField` ["maxBlockBodySize", "bytes"] + cMaxBlockBody <- cliResult `getField` ["maxBlockBodySize"] + assertEqual "maxBlockBodySize" oMaxBlockBody cMaxBlockBody + + oMaxTxSize <- ogmiosResult `getField` ["maxTransactionSize", "bytes"] + cMaxTxSize <- cliResult `getField` ["maxTxSize"] + assertEqual "maxTxSize" oMaxTxSize cMaxTxSize + + oStakeDeposit <- ogmiosResult `getField` ["stakeCredentialDeposit", "ada", "lovelace"] + cStakeDeposit <- cliResult `getField` ["stakeAddressDeposit"] + assertEqual "stakeCredentialDeposit" oStakeDeposit cStakeDeposit + + oPoolDeposit <- ogmiosResult `getField` ["stakePoolDeposit", "ada", "lovelace"] + cPoolDeposit <- cliResult `getField` ["stakePoolDeposit"] + assertEqual "stakePoolDeposit" oPoolDeposit cPoolDeposit + ] + +-- --------------------------------------------------------------------------- +-- Helpers +-- --------------------------------------------------------------------------- + +-- | Drill through nested objects along a path of keys, failing on the first +-- missing one. +getField :: Value -> [Key] -> IO Value +getField = foldM $ \v k -> + parseIO ("field " <> show k) (withObject "obj" (.: k)) v diff --git a/server/test/integration/src/Test/Integration/Query.hs b/server/test/integration/src/Test/Integration/Query.hs new file mode 100644 index 0000000000..ce6b26d3ba --- /dev/null +++ b/server/test/integration/src/Test/Integration/Query.hs @@ -0,0 +1,108 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +-- | Shared plumbing for the integration tests: querying ogmios over its +-- WebSocket JSON-RPC interface, querying cardano-cli, and the common +-- parsing/comparison helpers built on top of them. +module Test.Integration.Query + ( queryOgmios + , queryCli + , queryCliStdout + , assertSameSet + , parseIO + ) where + +import Control.Monad (when) +import Data.Aeson (Value(..), eitherDecode, encode, object, (.=)) +import Data.Aeson.Types (Parser, parseEither) +import Data.Set (Set) +import Data.Text (Text) +import System.IO (hClose, openTempFile) +import System.Process (callProcess, readProcess) +import Test.Tasty.HUnit (assertFailure) + +import qualified Data.Aeson.KeyMap as KM +import qualified Data.ByteString.Lazy as LBS +import qualified Data.Set as Set +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import qualified Network.WebSockets as WS + +import Test.Integration.Env (TestEnv(..), queryOgmiosRetry) + +-- | Send a single JSON-RPC request to ogmios and return its @result@ +-- payload. Connection and decoding failures are retried (via +-- 'queryOgmiosRetry'); a JSON-RPC error response fails the test. +queryOgmios :: TestEnv -> Text -> Maybe Value -> IO Value +queryOgmios env method params = do + resp <- queryOgmiosRetry (envOgmiosPort env) $ \port -> + WS.runClient "127.0.0.1" port "/" $ \conn -> do + WS.sendTextData conn $ encode $ object $ + [ "jsonrpc" .= ("2.0" :: Text) + , "method" .= method + , "id" .= Null + ] <> foldMap (\p -> [ "params" .= p ]) params + raw <- WS.receiveData conn + case eitherDecode raw of + Left err -> fail $ "Failed to decode ogmios response: " <> err + Right val -> pure val + case resp of + Object o + | Just result <- KM.lookup "result" o -> pure result + | Just err <- KM.lookup "error" o -> + assertFailure $ "Ogmios returned error: " <> show err + _ -> assertFailure $ "Unexpected ogmios response: " <> show resp + +-- | Run a cardano-cli query and decode the JSON it writes to a file. The +-- @--testnet-magic@, @--socket-path@ and @--out-file@ arguments are +-- appended to the given ones. Each call gets a fresh out-file under the +-- work directory (kept around for post-mortem debugging), so parallel +-- tests cannot clobber each other's output. +queryCli :: TestEnv -> [String] -> IO Value +queryCli env args = do + (outFile, h) <- openTempFile (envWorkDir env) "cli-query.json" + hClose h + callProcess "cardano-cli" $ args <> + [ "--testnet-magic", show (envTestnetMagic env) + , "--socket-path", envNodeSocket env + , "--out-file", outFile + ] + contents <- LBS.readFile outFile + case eitherDecode contents of + Left err -> fail $ "Failed to decode cardano-cli output: " <> err + Right val -> pure val + +-- | Like 'queryCli', for commands that only emit JSON on stdout and do +-- not support @--out-file@. +queryCliStdout :: TestEnv -> [String] -> IO Value +queryCliStdout env args = do + output <- readProcess "cardano-cli" + (args <> + [ "--testnet-magic", show (envTestnetMagic env) + , "--socket-path", envNodeSocket env + ]) + "" + case eitherDecode (LBS.fromStrict (T.encodeUtf8 (T.pack output))) of + Left err -> fail $ "Failed to decode cardano-cli output: " <> err + Right val -> pure val + +-- | Fail the test unless both sides observed the same set, printing the +-- elements only one side has. +assertSameSet :: (Ord a, Show a) => String -> Set a -> Set a -> IO () +assertSameSet what ogmiosSet cliSet = do + let ogmiosOnly = Set.difference ogmiosSet cliSet + cliOnly = Set.difference cliSet ogmiosSet + when (not (Set.null ogmiosOnly) || not (Set.null cliOnly)) $ + assertFailure $ unlines + [ what <> " differ:" + , " In Ogmios only (" <> show (Set.size ogmiosOnly) <> "):" + , concatMap (\e -> " " <> show e <> "\n") (Set.toList ogmiosOnly) + , " In cardano-cli only (" <> show (Set.size cliOnly) <> "):" + , concatMap (\e -> " " <> show e <> "\n") (Set.toList cliOnly) + ] + +-- | Run an aeson parser, failing the test on a parse error. +parseIO :: String -> (Value -> Parser a) -> Value -> IO a +parseIO what p val = case parseEither p val of + Left err -> assertFailure $ "Failed to parse " <> what <> ": " <> err + Right a -> pure a diff --git a/server/test/integration/src/Test/Integration/RewardAccountSummaries.hs b/server/test/integration/src/Test/Integration/RewardAccountSummaries.hs new file mode 100644 index 0000000000..4bee1f5623 --- /dev/null +++ b/server/test/integration/src/Test/Integration/RewardAccountSummaries.hs @@ -0,0 +1,64 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.RewardAccountSummaries + ( rewardAccountSummariesTests + ) where + +import Data.Aeson (Value, (.:), object, (.=)) +import Data.Aeson.Types (Parser, withArray, withObject) +import Data.Foldable (toList) +import System.FilePath (()) +import System.Process (readProcess) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertEqual, assertFailure, testCase) + +import Test.Integration.Env (TestEnv(..)) +import Test.Integration.Query (parseIO, queryCli, queryOgmios) + +rewardAccountSummariesTests :: IO TestEnv -> TestTree +rewardAccountSummariesTests getEnv = testGroup "RewardAccountSummaries" + [ testCase "rewardAccountSummaries match cardano-cli stake-address-info" $ do + env <- getEnv + + -- Derive stake address from the first delegator's staking key + let stakingVkey = envTestnetDir env "stake-delegators" + "delegator1" "staking.vkey" + output <- readProcess "cardano-cli" + [ "conway", "stake-address", "build" + , "--stake-verification-key-file", stakingVkey + , "--testnet-magic", show (envTestnetMagic env) + ] "" + stakeAddr <- case lines output of + (addr:_) -> pure addr + [] -> assertFailure "cardano-cli stake-address build returned empty output" + + ogmiosRewards <- parseIO "ogmios rewards" parseOgmiosRewards + =<< queryOgmios env "queryLedgerState/rewardAccountSummaries" + (Just (object [ "keys" .= [ stakeAddr ] ])) + + cliRewards <- parseIO "cli rewards" parseCliRewards + =<< queryCli env ["conway", "query", "stake-address-info", "--address", stakeAddr] + + assertEqual "reward balance (lovelace)" ogmiosRewards cliRewards + ] + +-- --------------------------------------------------------------------------- +-- Parsers +-- --------------------------------------------------------------------------- + +-- Ogmios returns an array of objects: [{ "credential": "...", "rewards": { "ada": { "lovelace": N } }, ... }] +parseOgmiosRewards :: Value -> Parser Integer +parseOgmiosRewards = withArray "summaries" $ \arr -> case toList arr of + [] -> fail "No reward account summaries returned" + (x:_) -> flip (withObject "entry") x $ \o -> do + rewards <- o .: "rewards" + ada <- rewards .: "ada" + ada .: "lovelace" + +-- cardano-cli returns: [ { "address": "...", "rewardAccountBalance": N, ... } ] +parseCliRewards :: Value -> Parser Integer +parseCliRewards = withArray "info" $ \arr -> case toList arr of + [] -> fail "No stake address info returned" + (x:_) -> flip (withObject "entry") x $ \o -> + o .: "rewardAccountBalance" diff --git a/server/test/integration/src/Test/Integration/StakePools.hs b/server/test/integration/src/Test/Integration/StakePools.hs new file mode 100644 index 0000000000..b9609211ba --- /dev/null +++ b/server/test/integration/src/Test/Integration/StakePools.hs @@ -0,0 +1,52 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.StakePools + ( stakePoolsTests + ) where + +import Data.Aeson (Value(..)) +import Data.Aeson.Types (Parser, withArray, withObject) +import Data.Foldable (toList) +import Data.Set (Set) +import Data.Text (Text) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (testCase) + +import qualified Data.Aeson.Key as Key +import qualified Data.Aeson.KeyMap as KM +import qualified Data.Set as Set + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (assertSameSet, parseIO, queryCli, queryOgmios) + +stakePoolsTests :: IO TestEnv -> TestTree +stakePoolsTests getEnv = testGroup "StakePools" + [ testCase "stakePool IDs match cardano-cli" $ do + env <- getEnv + + ogmiosSet <- parseIO "ogmios pool IDs" parseOgmiosPoolIds + =<< queryOgmios env "queryLedgerState/stakePools" Nothing + + cliSet <- parseIO "cli pool IDs" parseCliPoolIds + =<< queryCli env ["conway", "query", "stake-pools"] + + assertSameSet "Stake pool ID sets" ogmiosSet cliSet + ] + +-- --------------------------------------------------------------------------- +-- Parsers +-- --------------------------------------------------------------------------- + +-- Ogmios returns an object keyed by pool ID (bech32) +parseOgmiosPoolIds :: Value -> Parser (Set Text) +parseOgmiosPoolIds = withObject "pools" $ \o -> + pure $ Set.fromList $ map Key.toText (KM.keys o) + +-- cardano-cli stake-pools returns an array of bech32 pool IDs +parseCliPoolIds :: Value -> Parser (Set Text) +parseCliPoolIds = withArray "pools" $ \arr -> + fmap Set.fromList $ mapM parseString (toList arr) + where + parseString (String s) = pure s + parseString v = fail $ "Expected string, got: " <> show v diff --git a/server/test/integration/src/Test/Integration/TxSubmission.hs b/server/test/integration/src/Test/Integration/TxSubmission.hs new file mode 100644 index 0000000000..9ca9c3f1cb --- /dev/null +++ b/server/test/integration/src/Test/Integration/TxSubmission.hs @@ -0,0 +1,276 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +-- | Submitting transactions /through/ ogmios. +-- +-- cardano-node's tx-generator can be pointed at a submission endpoint +-- instead of a set of target nodes (@submissionEndpointProtocol: "Ogmios"@, +-- IntersectMBO/cardano-node#6609), in which case every transaction of the +-- run — the initial genesis expenditure included — travels over ogmios's +-- @submitTransaction@ rather than a local node socket. Its final phase pays +-- to the compiler's hardcoded \"BenchmarkingDone\" key, so new UTxOs at that +-- address are the proof that submissions were accepted /and/ made it into +-- blocks. +-- +-- Converted from @scripts\/test-txgen-submission.sh@: the binaries the script +-- resolved with nix now come from the @integration@ devshell, and the testnet +-- and ogmios instance are the ones 'Test.Integration.Env' already runs. +module Test.Integration.TxSubmission + ( txSubmissionTests + ) where + +import Control.Concurrent (threadDelay) +import Control.Monad (unless) +import Data.Aeson (Value(..), encode, object, (.=)) +import Data.Aeson.Types (withArray, withObject) +import Data.List (isInfixOf) +import Data.Text (Text) +import System.Directory (doesFileExist) +import System.Exit (ExitCode(..)) +import System.FilePath (()) +import System.IO (IOMode(..), withFile) +import System.Process + ( CreateProcess(cwd, std_err, std_out) + , StdStream(..) + , callProcess + , proc + , readProcess + , waitForProcess + , withCreateProcess + ) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (assertFailure, testCase) + +import qualified Data.Aeson.KeyMap as KM +import qualified Data.ByteString.Lazy as LBS +import qualified Data.Text as T + +import Test.Integration.Env (TestEnv(..)) +import Test.Integration.Query (parseIO, queryCli, queryOgmios) + +-- | The signing key the tx-generator's compiler pays to in its last phase. +-- The cborHex must match @keyBenchmarkDone@ in cardano-node's +-- @bench\/tx-generator\/src\/Cardano\/Benchmarking\/Compiler.hs@; its address +-- is derived below rather than hardcoded. +benchmarkDoneSKey :: Value +benchmarkDoneSKey = object + [ "type" .= ("PaymentSigningKeyShelley_ed25519" :: Text) + , "description" .= ("" :: Text) + , "cborHex" .= + ("582016ca4f13fa17557e56a7d0dd3397d747db8e1e22fdb5b9df638abdb680650d50" :: Text) + ] + +-- | How long to wait for the submitted transactions to show up in blocks. +awaitBlocksSeconds :: Int +awaitBlocksSeconds = 120 + +-- --------------------------------------------------------------------------- +-- Tests +-- --------------------------------------------------------------------------- + +txSubmissionTests :: IO TestEnv -> TestTree +txSubmissionTests getEnv = testGroup "TxSubmission" + [ -- This mutates the UTxO set, so it must run after the comparison tests + -- that assume a quiescent ledger — see the sequencing in "Main". + testCase "tx-generator submits through ogmios" $ do + env <- getEnv + benchAddr <- benchmarkDoneAddress env + + before <- utxoCountAt env benchAddr + putStrLn $ "[integration] " <> show before <> " UTxOs at " + <> T.unpack benchAddr <> " before submitting" + + logs <- runTxGeneratorViaOgmios env + assertSubmittedThroughEndpoint logs + + after <- awaitMoreUtxosAt env benchAddr before + putStrLn $ "[integration] " <> show after <> " UTxOs at the benchmark " + <> "address after submitting (" <> show (after - before) <> " new)" + ] + +-- --------------------------------------------------------------------------- +-- Running the generator +-- --------------------------------------------------------------------------- + +-- | Run a short tx-generator benchmark whose every phase submits through +-- ogmios, returning its combined output for inspection. +-- +-- Unlike the funding run in 'Test.Integration.Env', this one does not use +-- @--testnet-config-dir@: discovery always picks @utxo1@ and overrides the +-- config's own @sigKey@, and that key's genesis fund is already spent by the +-- funding run — a second expenditure of it would be rejected as a missing +-- input. The four connection settings discovery would supply are therefore +-- given explicitly, with a genesis key of our own (cardano-testnet seeds +-- three of them). +runTxGeneratorViaOgmios :: TestEnv -> IO String +runTxGeneratorViaOgmios env = do + let workDir = envWorkDir env + logsDir = workDir "logs" + configFile = workDir "tx-generator-submission.json" + sigKeyPath = envTestnetDir env "utxo-keys" "utxo2" "utxo.skey" + stdoutPath = logsDir "tx-generator-submission.stdout" + stderrPath = logsDir "tx-generator-submission.stderr" + + sigKeyExists <- doesFileExist sigKeyPath + unless sigKeyExists $ assertFailure $ + "Genesis signing key not found: " <> sigKeyPath + + LBS.writeFile configFile $ encode $ object + [ "tx_count" .= (10 :: Int) + , "tps" .= (2 :: Int) + , "inputs_per_tx" .= (2 :: Int) + , "outputs_per_tx" .= (2 :: Int) + , "tx_fee" .= (212345 :: Int) + , "min_utxo_value" .= (1000000 :: Int) + , "add_tx_size" .= (39 :: Int) + , "init_cooldown" .= (5 :: Int) + , "era" .= ("Conway" :: Text) + , "keepalive" .= (30 :: Int) + , "debugMode" .= False + , "plutus" .= Null + -- connection settings, see the note above + , "sigKey" .= sigKeyPath + , "localNodeSocketPath" .= envNodeSocket env + , "nodeConfigFile" .= envNodeConfig env + -- an endpoint replaces the target nodes as the submission target, and + -- the compiler rejects a config providing both. Spelling it out also + -- keeps a tx-generator without endpoint support (which ignores the two + -- keys below) from silently benchmarking node-to-node instead. + , "targetNodes" .= ([] :: [Value]) + , "submissionEndpointProtocol" .= ("Ogmios" :: Text) + , "submissionEndpointURI" .= + ("ws://127.0.0.1:" <> show (envOgmiosPort env)) + ] + + putStrLn "[integration] running tx-generator with ogmios submission..." + exitCode <- + withFile stdoutPath WriteMode $ \txgenStdout -> + withFile stderrPath WriteMode $ \txgenStderr -> + withCreateProcess + (proc "tx-generator" [ "json_highlevel", configFile ]) + { cwd = Just workDir + , std_out = UseHandle txgenStdout + , std_err = UseHandle txgenStderr + } + $ \_ _ _ ph -> waitForProcess ph + + out <- readFile stdoutPath + err <- readFile stderrPath + case exitCode of + ExitSuccess -> putStrLn "[integration] tx-generator finished." + ExitFailure c -> assertFailure $ unlines $ + [ "tx-generator exited with code " <> show c <> "." ] + <> endpointSupportHint (out <> err) <> logTails out err + pure (out <> err) + +-- | A tx-generator predating IntersectMBO/cardano-node#6609 parses +-- @targetNodes@ as a non-empty list, having no submission endpoint to replace +-- them with, so it dies on the empty one above. Name that cause: it is the +-- likely one, and its own diagnostic does not mention ogmios at all. +endpointSupportHint :: String -> [String] +endpointSupportHint logs + | "parsing NonEmpty failed" `isInfixOf` logs = + [ "That is what a tx-generator without Ogmios submission support makes" + , "of the empty targetNodes this test configures. It has to come from a" + , "cardano-node carrying IntersectMBO/cardano-node#6609: see the" + , "cardano-node-tx-generator flake input." + ] + | otherwise = [] + +-- | Guard against a false pass: a tx-generator predating +-- IntersectMBO/cardano-node#6609 ignores the two @submissionEndpoint*@ keys, +-- and would submit through a local node socket without ogmios ever seeing a +-- transaction. Such a version fails on the empty @targetNodes@ above, but +-- check its own report of the submission target too. +assertSubmittedThroughEndpoint :: String -> IO () +assertSubmittedThroughEndpoint logs = + unless (any (`isInfixOf` logs) evidence) $ assertFailure $ unlines $ + [ "tx-generator did not report submitting through an endpoint, so this run" + , "proves nothing about ogmios: a tx-generator without Ogmios submission" + , "support ignores the submissionEndpoint* configuration keys." + , "Expected its output to mention one of:" + ] <> map (" " <>) evidence <> [ "", "tx-generator output:", lastLines 30 logs ] + where + evidence = + [ "_nix_submissionEndpointProtocol = Just Ogmios" + , "submits through an endpoint" + ] + +-- --------------------------------------------------------------------------- +-- Counting UTxOs at the benchmark address +-- --------------------------------------------------------------------------- + +-- | Derive the address the benchmark's last phase pays to, the same way the +-- generator does: from 'benchmarkDoneSKey'. +benchmarkDoneAddress :: TestEnv -> IO Text +benchmarkDoneAddress env = do + let skeyFile = envWorkDir env "benchmark-done.skey" + vkeyFile = envWorkDir env "benchmark-done.vkey" + LBS.writeFile skeyFile (encode benchmarkDoneSKey) + callProcess "cardano-cli" + [ "key", "verification-key" + , "--signing-key-file", skeyFile + , "--verification-key-file", vkeyFile + ] + addr <- readProcess "cardano-cli" + [ "address", "build" + , "--payment-verification-key-file", vkeyFile + , "--testnet-magic", show (envTestnetMagic env) + ] "" + pure (T.strip (T.pack addr)) + +-- | How many UTxOs sit at an address, as agreed by ogmios and cardano-cli. +-- Disagreement fails the test: an address-filtered query is not covered by +-- the whole-UTxO comparison in "Test.Integration.Utxo". +utxoCountAt :: TestEnv -> Text -> IO Int +utxoCountAt env addr = do + ogmiosCount <- parseIO "ogmios UTxO" (withArray "utxo" (pure . length)) + =<< queryOgmios env "queryLedgerState/utxo" + (Just (object [ "addresses" .= [addr] ])) + + cliCount <- parseIO "cardano-cli UTxO" (withObject "utxo set" (pure . KM.size)) + =<< queryCli env [ "conway", "query", "utxo", "--address", T.unpack addr ] + + unless (ogmiosCount == cliCount) $ assertFailure $ unlines + [ "UTxO counts at " <> T.unpack addr <> " differ:" + , " ogmios: " <> show ogmiosCount + , " cardano-cli: " <> show cliCount + ] + pure ogmiosCount + +-- | Wait for the submitted transactions to be included in blocks, i.e. for +-- new UTxOs to appear at the benchmark address. Once one shows up, settle +-- briefly and report the final count. +awaitMoreUtxosAt :: TestEnv -> Text -> Int -> IO Int +awaitMoreUtxosAt env addr before = go awaitBlocksSeconds + where + go n + | n <= 0 = assertFailure $ unlines + [ "No new UTxOs appeared at " <> T.unpack addr <> " within " + <> show awaitBlocksSeconds <> "s." + , "tx-generator reported every submission as accepted, so the" + , "transactions should have made it into blocks by now." + , " UTxOs before: " <> show before + ] + | otherwise = do + count <- utxoCountAt env addr + if count > before + then threadDelay 5000000 >> utxoCountAt env addr + else threadDelay 2000000 >> go (n - 2) + +-- --------------------------------------------------------------------------- +-- Reporting +-- --------------------------------------------------------------------------- + +-- | The tail of both logs, for a failure message. Worth spelling out: a +-- tx-generator that fails before its tracer is initialized can die without +-- diagnostics of its own (see the recursion in cardano-node's +-- @Cardano.Benchmarking.Script.Env.getBenchTracers@). +logTails :: String -> String -> [String] +logTails out err = + [ "", "tx-generator stdout (last 30 lines):", lastLines 30 out + , "", "tx-generator stderr (last 20 lines):", lastLines 20 err + ] + +lastLines :: Int -> String -> String +lastLines n = unlines . reverse . take n . reverse . lines diff --git a/server/test/integration/src/Test/Integration/Utxo.hs b/server/test/integration/src/Test/Integration/Utxo.hs new file mode 100644 index 0000000000..a97fda6f6f --- /dev/null +++ b/server/test/integration/src/Test/Integration/Utxo.hs @@ -0,0 +1,101 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Test.Integration.Utxo + ( utxoTests + ) where + +import Control.Monad (forM) +import Data.Aeson (Value, (.:), object) +import Data.Aeson.Types (Parser, withArray, withObject) +import Data.Foldable (toList) +import Data.Set (Set) +import Data.Text (Text) +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.HUnit (testCase) +import Text.Read (readMaybe) + +import qualified Data.Aeson.Key as Key +import qualified Data.Aeson.KeyMap as KM +import qualified Data.Set as Set +import qualified Data.Text as T + +import Test.Integration.Env (TestEnv) +import Test.Integration.Query (assertSameSet, parseIO, queryCli, queryOgmios) + +-- --------------------------------------------------------------------------- +-- Normalized UTxO representation +-- --------------------------------------------------------------------------- + +data NormalizedUtxo = NormalizedUtxo + { nuTxId :: !Text + , nuTxIndex :: !Int + , nuAddress :: !Text + , nuLovelace :: !Integer + } deriving (Eq, Ord, Show) + +-- --------------------------------------------------------------------------- +-- Tests +-- --------------------------------------------------------------------------- + +utxoTests :: IO TestEnv -> TestTree +utxoTests getEnv = testGroup "UTxO" + [ testCase "WholeUtxo matches cardano-cli" $ do + env <- getEnv + + ogmiosUtxos <- parseIO "ogmios UTxO" parseOgmiosUtxo + =<< queryOgmios env "queryLedgerState/utxo" (Just (object [])) + + cliUtxos <- parseIO "cardano-cli UTxO" parseCardanoCliUtxo + =<< queryCli env ["conway", "query", "utxo", "--whole-utxo"] + + assertSameSet "UTxO sets" ogmiosUtxos cliUtxos + ] + +-- --------------------------------------------------------------------------- +-- Parsers +-- --------------------------------------------------------------------------- + +parseOgmiosUtxo :: Value -> Parser (Set NormalizedUtxo) +parseOgmiosUtxo = withArray "utxo" $ \arr -> do + entries <- mapM parseEntry (toList arr) + pure (Set.fromList entries) + where + parseEntry = withObject "utxo entry" $ \o -> do + tx <- o .: "transaction" + txId <- tx .: "id" + idx <- o .: "index" + addr <- o .: "address" + val <- o .: "value" + ada <- val .: "ada" + lv <- ada .: "lovelace" + pure NormalizedUtxo + { nuTxId = txId + , nuTxIndex = idx + , nuAddress = addr + , nuLovelace = lv + } + +parseCardanoCliUtxo :: Value -> Parser (Set NormalizedUtxo) +parseCardanoCliUtxo = withObject "utxo set" $ \o -> do + entries <- forM (KM.toList o) $ \(key, val) -> do + let keyText = Key.toText key + (txId, rest) = T.breakOn "#" keyText + idxText = T.drop 1 rest + idx <- case readMaybe (T.unpack idxText) of + Just n -> pure n + Nothing -> fail $ "Invalid UTxO key (expected txid#index): " <> T.unpack keyText + parseOutput txId idx val + pure (Set.fromList entries) + +parseOutput :: Text -> Int -> Value -> Parser NormalizedUtxo +parseOutput txId idx = withObject "utxo output" $ \o -> do + addr <- o .: "address" + val <- o .: "value" + lv <- val .: "lovelace" + pure NormalizedUtxo + { nuTxId = txId + , nuTxIndex = idx + , nuAddress = addr + , nuLovelace = lv + } diff --git a/server/test/unit/Test/Path/Util.hs b/server/test/unit/Test/Path/Util.hs index 548138152b..8cd548e0f7 100644 --- a/server/test/unit/Test/Path/Util.hs +++ b/server/test/unit/Test/Path/Util.hs @@ -2,6 +2,8 @@ -- License, v. 2.0. If a copy of the MPL was not distributed with this -- file, You can obtain one at http://mozilla.org/MPL/2.0/. +{-# LANGUAGE TemplateHaskell #-} + module Test.Path.Util ( getProjectRoot ) where @@ -17,6 +19,20 @@ import Language.Haskell.TH.Syntax , liftData ) +import qualified System.Environment as Env +import qualified System.IO.Unsafe as Unsafe + +-- | Absolute path to the directory containing the project's .cabal file. +-- +-- It is resolved at compile-time, so it is only valid where (and while) the +-- source tree that produced the test binary exists; when the binary runs +-- elsewhere (e.g. a Nix check derivation, which builds and runs tests in +-- separate sandboxes), the OGMIOS_TEST_PROJECT_ROOT environment variable +-- overrides it at run-time. getProjectRoot :: Q Exp -getProjectRoot = - liftData =<< makeRelativeToProject "" +getProjectRoot = do + compileTimeRoot <- makeRelativeToProject "" + [| Unsafe.unsafePerformIO + ( fromMaybe $(liftData compileTimeRoot) + <$> Env.lookupEnv "OGMIOS_TEST_PROJECT_ROOT" + ) |]