diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..c4367cb --- /dev/null +++ b/.envrc @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Ensure git submodules are initialized before nix evaluates the flake. +# The flake declares `self.submodules = true`, so Nix's git fetcher reads +# submodule contents during source fetching — which fails if they're not +# present. This runs before `use flake`, breaking the chicken-and-egg. +git submodule update --init --recursive + +use flake diff --git a/cabal.project b/cabal.project index bab3c3d..fb53b03 100644 --- a/cabal.project +++ b/cabal.project @@ -10,8 +10,8 @@ repository cardano-haskell-packages d4a35cd3121aa00d18544bb0ac01c3e1691d618f462c46129271bccf39f7e8ee index-state: - , hackage.haskell.org 2025-09-11T07:59:00Z - , cardano-haskell-packages 2025-09-11T08:36:14Z + , hackage.haskell.org 2026-05-08T22:40:19Z + , cardano-haskell-packages 2026-05-08T13:26:45Z packages: ./ @@ -46,26 +46,29 @@ package direct-sqlite flags: +nomutex constraints: - , any.cardano-node == 10.5.1 + , any.cardano-node == 11.0.1 - , any.cardano-ledger-core == 1.17.0.0 - , any.cardano-ledger-shelley == 1.16.0.0 - , any.cardano-ledger-conway == 1.19.0.0 + , any.cardano-ledger-conway >= 1.22.1.0 - , any.ouroboros-consensus == 0.27.0.0 - , any.ouroboros-consensus-cardano == 0.25.1.0 - , any.ouroboros-network == 0.21.3.0 + , any.ouroboros-consensus ^>= 3.0.1 + , any.ouroboros-network ^>= 1.1 - , any.io-classes == 1.5.0.0 - , any.io-classes-mtl == 0.1.2.0 + , any.io-classes ^>= 1.8 , any.formatting == 7.2.0 + , any.validation < 1.2 , any.text source + -- Use serial block IO instead of io_uring to avoid requiring liburing + , any.blockio +serialblockio + , direct-sqlite == 2.3.29.1 , sqlite-simple == 0.4.19.0.1 allow-newer: *:formatting + , katip:Win32 + , io-sim:time + , io-classes:time -- NOTE update hash using -- nix-prefetch-git https://github.com/CardanoSolutions/ogmios.git --rev --fetch-submodules --quiet | jq '.hash' | tail -c +9 | head -c -2 diff --git a/kupo.cabal b/kupo.cabal index 3192190..37c6415 100644 --- a/kupo.cabal +++ b/kupo.cabal @@ -1,6 +1,6 @@ -cabal-version: 1.12 +cabal-version: 3.0 --- This file has been generated from package.yaml by hpack version 0.38.2. +-- This file has been generated from package.yaml by hpack version 0.38.3. -- -- see: https://github.com/sol/hpack @@ -131,6 +131,8 @@ library Kupo.Version.TH other-modules: Paths_kupo + autogen-modules: + Paths_kupo hs-source-dirs: src default-extensions: @@ -184,6 +186,7 @@ library , bytestring , cardano-crypto-class , cardano-crypto-wrapper + , cardano-diffusion , cardano-ledger-allegra , cardano-ledger-alonzo , cardano-ledger-api @@ -192,6 +195,7 @@ library , cardano-ledger-byron , cardano-ledger-conway , cardano-ledger-core + , cardano-ledger-dijkstra , cardano-ledger-mary , cardano-ledger-shelley , cardano-slotting @@ -215,12 +219,11 @@ library , network-mux , optparse-applicative , ouroboros-consensus - , ouroboros-consensus-cardano - , ouroboros-consensus-diffusion - , ouroboros-network - , ouroboros-network-api - , ouroboros-network-framework - , ouroboros-network-protocols + , ouroboros-consensus:cardano + , ouroboros-consensus:diffusion + , ouroboros-network:api + , ouroboros-network:framework + , ouroboros-network:protocols , prometheus , relude , resource-pool @@ -255,6 +258,8 @@ executable kupo main-is: Main.hs other-modules: Paths_kupo + autogen-modules: + Paths_kupo hs-source-dirs: app default-extensions: @@ -332,6 +337,8 @@ test-suite unit Test.Kupo.OptionsSpec Test.KupoSpec Paths_kupo + autogen-modules: + Paths_kupo hs-source-dirs: test default-extensions: @@ -388,14 +395,15 @@ test-suite unit , http-media , http-types , io-classes + , io-classes:si-timers , io-sim , kupo , lens-aeson + , network , openapi3 , process , quickcheck-state-machine , relude - , si-timers , sqlite-simple , stm , temporary diff --git a/package.yaml b/package.yaml index fb57ab5..229eed2 100644 --- a/package.yaml +++ b/package.yaml @@ -71,6 +71,7 @@ library: - cardano-ledger-byron - cardano-ledger-conway - cardano-ledger-core + - cardano-ledger-dijkstra - cardano-ledger-mary - cardano-ledger-shelley - cardano-slotting @@ -93,13 +94,13 @@ library: - modern-uri - network-mux - optparse-applicative + - cardano-diffusion - ouroboros-consensus - - ouroboros-consensus-cardano - - ouroboros-consensus-diffusion - - ouroboros-network - - ouroboros-network-api - - ouroboros-network-framework - - ouroboros-network-protocols + - ouroboros-consensus:cardano + - ouroboros-consensus:diffusion + - ouroboros-network:api + - ouroboros-network:framework + - ouroboros-network:protocols - prometheus - relude - resource-pool @@ -139,6 +140,7 @@ tests: - http-types - io-classes - io-sim + - network - kupo - lens-aeson - openapi3 @@ -146,7 +148,7 @@ tests: - QuickCheck - quickcheck-state-machine - relude - - si-timers + - io-classes:si-timers - sqlite-simple - stm - temporary diff --git a/perSystem/musl.nix b/perSystem/musl.nix new file mode 100644 index 0000000..f6202bf --- /dev/null +++ b/perSystem/musl.nix @@ -0,0 +1,17 @@ +{ + perSystem = { + project, + system, + lib, + ... + }: let + muslProject = project.projectCross.${ + if system == "x86_64-linux" + then "musl64" + else "aarch64-multiplatform-musl" + }; + muslExes = muslProject.hsPkgs.kupo.components.exes; + in { + packages.kupo-exe-musl = muslExes.kupo; + }; +} diff --git a/perSystem/packages.nix b/perSystem/packages.nix index 5a86161..37a5736 100644 --- a/perSystem/packages.nix +++ b/perSystem/packages.nix @@ -4,8 +4,12 @@ kupo = hsPkgs.kupo; in { - packages.kupo = kupo.components.library; - packages.kupo-exe = kupo.components.exes.kupo; + packages = { + default = kupo.components.exes.kupo; + kupo = kupo.components.library; + kupo-exe = kupo.components.exes.kupo; + }; + checks.kupo-unit = kupo.checks.unit; }; } diff --git a/perSystem/project.nix b/perSystem/project.nix index c12e2bf..07ddcf0 100644 --- a/perSystem/project.nix +++ b/perSystem/project.nix @@ -1,71 +1,107 @@ -{ inputs, self, ... }: - { - perSystem = { pkgs, lib, ... }: - let - project = pkgs.haskell-nix.cabalProject' ({ config, pkgs, ... }: { - src = pkgs.haskell-nix.haskellLib.cleanSourceWith { - name = "kupo-src"; - src = self; - # Filter out package.yaml files so plan-to-nix uses the - # pre-generated .cabal files instead. The package.yaml files - # `!include` .hpack.config.yaml via relative paths that the - # haskell.nix sandbox can't always resolve. - filter = path: type: - builtins.all (x: x) [ - (baseNameOf path != "package.yaml") - ]; - }; - name = "kupo"; - compiler-nix-name = lib.mkDefault "ghc984"; + inputs, + self, + ... +}: { + perSystem = { + pkgs, + lib, + ... + }: let + project = pkgs.haskell-nix.cabalProject' ({ + config, + pkgs, + ... + }: { + src = pkgs.haskell-nix.haskellLib.cleanSourceWith { + name = "kupo-src"; + src = self; + # Filter out package.yaml files so plan-to-nix uses the + # pre-generated .cabal files instead. The package.yaml files + # `!include` .hpack.config.yaml via relative paths that the + # haskell.nix sandbox can't always resolve. + filter = path: type: + builtins.all (x: x) [ + (baseNameOf path != "package.yaml") + ]; + }; + name = "kupo"; + compiler-nix-name = lib.mkDefault "ghc984"; - inputMap = { - "https://input-output-hk.github.io/cardano-haskell-packages" = inputs.CHaP; - }; + inputMap = { + "https://input-output-hk.github.io/cardano-haskell-packages" = inputs.CHaP; + }; - # Mirrors source-repository-package entries in cabal.project so - # haskell.nix can fetch them deterministically. - sha256map = { - "https://github.com/CardanoSolutions/ogmios"."ae876badb138f42dcd6d2389734b0c15502684ed" = "sha256-xkOfOdX6Dxi7+VW78Tk3n3MoguIg39pKdxiNVfdeEwE="; - "https://github.com/CardanoSolutions/sqlite-simple"."08015be2ee52a7e67159b6b0c476bd3e0a2e0b87" = "1ahpjycsfibv09kzgfbm4i55z4nz1p3rvnmfwwwraxy45n1ivl85"; - "https://github.com/CardanoSolutions/direct-sqlite"."2b14a78cb73805e2e5d84354230e872a223faa39" = "1lwaariy0zjjh006ll1zbpdi9sphyqmcbbxhb0rj99nii5s91fd7"; - "https://github.com/CardanoSolutions/text-ansi"."e204822d2f343b2d393170a2ec46ee935571345c" = "16ki7wsf7wivxn65acv4hxwfrzmphq4zp61lpxwzqkgrg8shi8bv"; - }; + # Mirrors source-repository-package entries in cabal.project so + # haskell.nix can fetch them deterministically. + sha256map = { + "https://github.com/CardanoSolutions/ogmios"."ae876badb138f42dcd6d2389734b0c15502684ed" = "sha256-xkOfOdX6Dxi7+VW78Tk3n3MoguIg39pKdxiNVfdeEwE="; + "https://github.com/CardanoSolutions/sqlite-simple"."08015be2ee52a7e67159b6b0c476bd3e0a2e0b87" = "1ahpjycsfibv09kzgfbm4i55z4nz1p3rvnmfwwwraxy45n1ivl85"; + "https://github.com/CardanoSolutions/direct-sqlite"."2b14a78cb73805e2e5d84354230e872a223faa39" = "1lwaariy0zjjh006ll1zbpdi9sphyqmcbbxhb0rj99nii5s91fd7"; + "https://github.com/CardanoSolutions/text-ansi"."e204822d2f343b2d393170a2ec46ee935571345c" = "16ki7wsf7wivxn65acv4hxwfrzmphq4zp61lpxwzqkgrg8shi8bv"; + }; - modules = [ - { - doHaddock = false; - packages.kupo.ghcOptions = [ "-Werror" ]; - # GHC 9.8 enabled -Wx-partial by default; kupo was written for - # 9.6 and uses `Prelude.head` in a handful of test spots. - # Silence it under -Werror until the source is updated. - packages.kupo.components.tests.unit.ghcOptions = [ "-Wno-x-partial" ]; + modules = [ + { + doHaddock = false; + packages.kupo.ghcOptions = ["-Werror"]; + # GHC 9.8 enabled -Wx-partial by default; kupo was written for + # 9.6 and uses `Prelude.head` in a handful of test spots. + # Silence it under -Werror until the source is updated. + packages.kupo.components.tests.unit.ghcOptions = ["-Wno-x-partial"]; - # Tests use relative paths like `./test/vectors/...` and - # `./config/network/.../config.json`. The default test runCommand - # CWD is empty, so populate it with the vectors tree and the - # cardano-configurations submodule before invoking the binary. - packages.kupo.components.tests.unit.preCheck = '' - cp -r ${self}/test/vectors ./test/vectors - cp -r ${self}/config ./config - ''; - } - ({ pkgs, ... }: { - # Use the VRF fork of libsodium - packages = { - cardano-crypto-praos.components.library.pkgconfig = pkgs.lib.mkForce [ - [ pkgs.libsodium-vrf ] - ]; - cardano-crypto-class.components.library.pkgconfig = pkgs.lib.mkForce [ - [ pkgs.libsodium-vrf pkgs.secp256k1 pkgs.libblst ] - ]; - }; - }) - ]; - }); - in - { - _module.args.hsPkgs = project.hsPkgs; - _module.args.shellFor = args: project.shellFor args; - }; + # Tests use relative paths like `./test/vectors/...` and + # `./config/network/.../config.json`. The default test runCommand + # CWD is empty, so populate it with the vectors tree and the + # cardano-configurations submodule before invoking the binary. + packages.kupo.components.tests.unit.preCheck = '' + cp -r ${self}/test/vectors ./test/vectors + cp -r ${self}/config ./config + ''; + } + ({pkgs, ...}: { + # Use the VRF fork of libsodium + packages = { + cardano-crypto-praos.components.library.pkgconfig = pkgs.lib.mkForce [ + [pkgs.libsodium-vrf] + ]; + cardano-crypto-class.components.library.pkgconfig = pkgs.lib.mkForce [ + [pkgs.libsodium-vrf pkgs.secp256k1 pkgs.libblst] + ]; + }; + }) + # Musl libc fully static build + ({ + config, + lib, + pkgs, + ... + }: { + options.packages = lib.genAttrs config.package-keys (_: + lib.mkOption { + type = lib.types.submodule ( + { + config, + lib, + pkgs, + ... + }: + lib.mkIf (pkgs.stdenv.hostPlatform.isMusl && config.package.isLocal) + { + enableShared = true; # TH code breaks if this is false + enableStatic = true; + } + ); + }); + config = lib.mkIf pkgs.stdenv.hostPlatform.isMusl { + doHaddock = false; + }; + }) + ]; + }); + in { + _module.args.project = project; + _module.args.hsPkgs = project.hsPkgs; + _module.args.shellFor = args: project.shellFor args; + }; } diff --git a/src/Kupo/Control/MonadOuroboros.hs b/src/Kupo/Control/MonadOuroboros.hs index 7583f43..61bec00 100644 --- a/src/Kupo/Control/MonadOuroboros.hs +++ b/src/Kupo/Control/MonadOuroboros.hs @@ -35,6 +35,7 @@ import Kupo.Control.MonadThrow import Network.Mux ( StartOnDemandOrEagerly (..) ) +import qualified Network.Mux import Ouroboros.Consensus.Byron.Ledger.Config ( CodecConfig (..) ) @@ -74,7 +75,7 @@ import Ouroboros.Network.Mux , OuroborosApplication (..) , RunMiniProtocol (..) ) -import Ouroboros.Network.NodeToClient +import Cardano.Network.NodeToClient ( NetworkConnectTracers (..) , NodeToClientVersion (..) , NodeToClientVersionData (..) @@ -119,7 +120,7 @@ instance MonadOuroboros IO where connectTo (mkLocalSnocket iocp) tracers versions socket >>= either throwIO return where tracers = NetworkConnectTracers - { nctMuxTracer = nullTracer + { nctMuxTracers = Network.Mux.nullTracers , nctHandshakeTracer = nullTracer } @@ -167,7 +168,7 @@ codecs epochSlots nodeToClientV = supportedVersions = supportedNodeToClientVersions (Proxy @(BlockT IO)) cfg = - CardanoCodecConfig byron shelley allegra mary alonzo babbage conway + CardanoCodecConfig byron shelley allegra mary alonzo babbage conway dijkstra where byron = ByronCodecConfig epochSlots shelley = ShelleyCodecConfig @@ -176,3 +177,4 @@ codecs epochSlots nodeToClientV = alonzo = ShelleyCodecConfig babbage = ShelleyCodecConfig conway = ShelleyCodecConfig + dijkstra = ShelleyCodecConfig diff --git a/src/Kupo/Data/Cardano.hs b/src/Kupo/Data/Cardano.hs index 59e9ed1..64d49ad 100644 --- a/src/Kupo/Data/Cardano.hs +++ b/src/Kupo/Data/Cardano.hs @@ -99,7 +99,6 @@ import Kupo.Data.Cardano.Value import qualified Cardano.Chain.UTxO as Ledger.Byron import qualified Cardano.Ledger.Alonzo.Core as Ledger -import qualified Cardano.Ledger.Alonzo.Tx as Ledger import qualified Cardano.Ledger.Alonzo.TxWits as Ledger import qualified Cardano.Ledger.Babbage.Core as Ledger import qualified Cardano.Ledger.Block as Ledger @@ -184,17 +183,19 @@ instance IsBlock Block where in foldrWithIndex ignoreProtocolTxs result (extractTxs blk) BlockShelley (ShelleyBlock (Ledger.Block _ txs) _) -> - foldrWithIndex (\ix -> fn ix . TransactionShelley) result (Ledger.fromTxSeq txs) + foldrWithIndex (\ix -> fn ix . TransactionShelley) result (txs ^. Ledger.txSeqBlockBodyL) BlockAllegra (ShelleyBlock (Ledger.Block _ txs) _) -> - foldrWithIndex (\ix -> fn ix . TransactionAllegra) result (Ledger.fromTxSeq txs) + foldrWithIndex (\ix -> fn ix . TransactionAllegra) result (txs ^. Ledger.txSeqBlockBodyL) BlockMary (ShelleyBlock (Ledger.Block _ txs) _) -> - foldrWithIndex (\ix -> fn ix . TransactionMary) result (Ledger.fromTxSeq txs) + foldrWithIndex (\ix -> fn ix . TransactionMary) result (txs ^. Ledger.txSeqBlockBodyL) BlockAlonzo (ShelleyBlock (Ledger.Block _ txs) _) -> - foldrWithIndex (\ix -> fn ix . TransactionAlonzo) result (Ledger.fromTxSeq txs) + foldrWithIndex (\ix -> fn ix . TransactionAlonzo) result (txs ^. Ledger.txSeqBlockBodyL) BlockBabbage (ShelleyBlock (Ledger.Block _ txs) _) -> - foldrWithIndex (\ix -> fn ix . TransactionBabbage) result (Ledger.fromTxSeq txs) + foldrWithIndex (\ix -> fn ix . TransactionBabbage) result (txs ^. Ledger.txSeqBlockBodyL) BlockConway (ShelleyBlock (Ledger.Block _ txs) _) -> - foldrWithIndex (\ix -> fn ix . TransactionConway) result (Ledger.fromTxSeq txs) + foldrWithIndex (\ix -> fn ix . TransactionConway) result (txs ^. Ledger.txSeqBlockBodyL) + BlockDijkstra (ShelleyBlock (Ledger.Block _ txs) _) -> + foldrWithIndex (\ix -> fn ix . TransactionDijkstra) result (txs ^. Ledger.txSeqBlockBodyL) spentInputs :: Transaction @@ -226,6 +227,12 @@ instance IsBlock Block where tx ^. Ledger.bodyTxL . Ledger.inputsTxBodyL Ledger.IsValid False -> tx ^. Ledger.bodyTxL . Ledger.collateralInputsTxBodyL + TransactionDijkstra tx -> + case tx ^. Ledger.isValidTxL of + Ledger.IsValid True -> + tx ^. Ledger.bodyTxL . Ledger.inputsTxBodyL + Ledger.IsValid False -> + tx ^. Ledger.bodyTxL . Ledger.collateralInputsTxBodyL where transformByron (Ledger.Byron.TxInUtxo txId ix) = mkOutputReference @@ -309,12 +316,29 @@ instance IsBlock Block where in case tx ^. Ledger.isValidTxL of Ledger.IsValid True -> - traverseAndTransform identity txId meta 0 outs + traverseAndTransform fromConwayOutput txId meta 0 outs Ledger.IsValid False -> -- From Conway formal specification: -- -- Note that the new collOuts function generates a single output -- with an index |txouts{txb}|. + let start = fromIntegral (length outs) in + case body ^. Ledger.collateralReturnTxBodyL of + SNothing -> + [] + SJust r -> + traverseAndTransform fromConwayOutput txId meta start (r :<| mempty) + TransactionDijkstra tx -> + let + body = tx ^. Ledger.bodyTxL + txId = Ledger.txIdTxBody @DijkstraEra body + outs = body ^. Ledger.outputsTxBodyL + meta = tx ^. Ledger.auxDataTxL & strictMaybe emptyMetadata fromDijkstraMetadata + in + case tx ^. Ledger.isValidTxL of + Ledger.IsValid True -> + traverseAndTransform identity txId meta 0 outs + Ledger.IsValid False -> let start = fromIntegral (length outs) in case body ^. Ledger.collateralReturnTxBodyL of SNothing -> @@ -381,6 +405,8 @@ instance IsBlock Block where fromBabbageData <$> Ledger.unTxDats (tx ^. Ledger.witsTxL . Ledger.datsTxWitsL) TransactionConway tx -> fromConwayData <$> Ledger.unTxDats (tx ^. Ledger.witsTxL . Ledger.datsTxWitsL) + TransactionDijkstra tx -> + fromDijkstraData <$> Ledger.unTxDats (tx ^. Ledger.witsTxL . Ledger.datsTxWitsL) witnessedScripts :: Transaction @@ -413,6 +439,13 @@ instance IsBlock Block where & scriptsFromOutputs (fromBabbageOutput <$> tx ^. Ledger.bodyTxL . Ledger.outputsTxBodyL) TransactionConway tx -> + ( fromConwayScript <$> tx ^. Ledger.witsTxL . Ledger.scriptTxWitsL + ) & strictMaybe identity + (scriptFromAlonzoAuxiliaryData fromConwayScript) + (tx ^. Ledger.auxDataTxL) + & scriptsFromOutputs + (fromConwayOutput <$> tx ^. Ledger.bodyTxL . Ledger.outputsTxBodyL) + TransactionDijkstra tx -> ( tx ^. Ledger.witsTxL . Ledger.scriptTxWitsL ) & strictMaybe identity (scriptFromAlonzoAuxiliaryData identity) @@ -468,6 +501,13 @@ instance IsBlock Block where SJust auxData -> let meta = fromConwayMetadata auxData in Just (hashMetadata meta, meta) + TransactionDijkstra tx -> + case tx ^. Ledger.auxDataTxL of + SNothing -> + Nothing + SJust auxData -> + let meta = fromDijkstraMetadata auxData + in Just (hashMetadata meta, meta) spendRedeemer :: Transaction @@ -489,3 +529,5 @@ instance IsBlock Block where Just (RedeemersBabbage (tx ^. Ledger.witsTxL . Ledger.rdmrsTxWitsL)) TransactionConway tx -> Just (RedeemersConway (tx ^. Ledger.witsTxL . Ledger.rdmrsTxWitsL)) + TransactionDijkstra tx -> + Just (RedeemersDisjkstra (tx ^. Ledger.witsTxL . Ledger.rdmrsTxWitsL)) diff --git a/src/Kupo/Data/Cardano/AssetName.hs b/src/Kupo/Data/Cardano/AssetName.hs index ccb3f5a..e0c6927 100644 --- a/src/Kupo/Data/Cardano/AssetName.hs +++ b/src/Kupo/Data/Cardano/AssetName.hs @@ -2,10 +2,6 @@ module Kupo.Data.Cardano.AssetName where import Kupo.Prelude -import Ouroboros.Consensus.Util - ( eitherToMaybe - ) - import qualified Cardano.Ledger.Mary.Value as Ledger import qualified Data.ByteString as BS diff --git a/src/Kupo/Data/Cardano/BinaryData.hs b/src/Kupo/Data/Cardano/BinaryData.hs index fae48be..d84f1ed 100644 --- a/src/Kupo/Data/Cardano/BinaryData.hs +++ b/src/Kupo/Data/Cardano/BinaryData.hs @@ -12,7 +12,7 @@ import qualified Data.Aeson as Json import qualified Data.Aeson.Encoding as Json type BinaryData = - Ledger.BinaryData ConwayEra + Ledger.BinaryData DijkstraEra type BinaryDataHash = DatumHash @@ -54,24 +54,43 @@ unsafeBinaryDataFromBytes = {-# INLINABLE unsafeBinaryDataFromBytes #-} fromAlonzoData - :: HasCallStack - => Ledger.Data AlonzoEra + :: Ledger.Data AlonzoEra -> BinaryData fromAlonzoData = - unsafeBinaryDataFromBytes . Ledger.originalBytes + Ledger.dataToBinaryData . Ledger.upgradeData {-# INLINEABLE fromAlonzoData #-} fromBabbageData :: Ledger.Data BabbageEra -> BinaryData fromBabbageData = - Ledger.dataToBinaryData - . Ledger.upgradeData + Ledger.dataToBinaryData . Ledger.upgradeData {-# INLINEABLE fromBabbageData #-} fromConwayData :: Ledger.Data ConwayEra -> BinaryData fromConwayData = - Ledger.dataToBinaryData + Ledger.dataToBinaryData . Ledger.upgradeData {-# INLINEABLE fromConwayData #-} + +fromDijkstraData + :: Ledger.Data DijkstraEra + -> BinaryData +fromDijkstraData = + Ledger.dataToBinaryData +{-# INLINEABLE fromDijkstraData #-} + +fromDijkstraBinaryData + :: Ledger.BinaryData DijkstraEra + -> BinaryData +fromDijkstraBinaryData = + identity +{-# INLINEABLE fromDijkstraBinaryData #-} + +toDijkstraBinaryData + :: BinaryData + -> Ledger.BinaryData DijkstraEra +toDijkstraBinaryData = + identity +{-# INLINEABLE toDijkstraBinaryData #-} diff --git a/src/Kupo/Data/Cardano/Datum.hs b/src/Kupo/Data/Cardano/Datum.hs index d4de93a..db9788d 100644 --- a/src/Kupo/Data/Cardano/Datum.hs +++ b/src/Kupo/Data/Cardano/Datum.hs @@ -18,24 +18,25 @@ data Datum | Inline !(Either DatumHash BinaryData) deriving (Generic, Show, Eq, Ord) -toConwayDatum + +fromDijkstraDatum + :: Ledger.Datum DijkstraEra + -> Datum +fromDijkstraDatum = \case + Ledger.NoDatum -> NoDatum + Ledger.DatumHash ref -> Reference (Left ref) + Ledger.Datum bin -> Inline (Right bin) + +toDijkstraDatum :: Datum - -> Ledger.Datum ConwayEra -toConwayDatum = \case + -> Ledger.Datum DijkstraEra +toDijkstraDatum = \case NoDatum -> Ledger.NoDatum Reference (Left ref) -> Ledger.DatumHash ref Reference (Right bin) -> Ledger.Datum bin Inline (Left ref) -> Ledger.DatumHash ref Inline (Right bin) -> Ledger.Datum bin -fromConwayDatum - :: Ledger.Datum ConwayEra - -> Datum -fromConwayDatum = \case - Ledger.NoDatum -> NoDatum - Ledger.DatumHash ref -> Reference (Left ref) - Ledger.Datum bin -> Inline (Right bin) - getBinaryData :: Datum -> Maybe BinaryData diff --git a/src/Kupo/Data/Cardano/Metadata.hs b/src/Kupo/Data/Cardano/Metadata.hs index 9abf045..bd56e89 100644 --- a/src/Kupo/Data/Cardano/Metadata.hs +++ b/src/Kupo/Data/Cardano/Metadata.hs @@ -22,13 +22,9 @@ import Kupo.Data.Cardano.MetadataHash ( MetadataHash , metadataHashToJson ) -import Ouroboros.Consensus.Util - ( eitherToMaybe - ) - import qualified Cardano.Ledger.Allegra.Scripts as Ledger.Allegra -import qualified Cardano.Ledger.Alonzo.TxAuxData as Ledger.Alonzo import qualified Cardano.Ledger.Core as Ledger +import qualified Cardano.Ledger.Dijkstra.Scripts as Ledger.Dijkstra import qualified Cardano.Ledger.Shelley.TxAuxData as Ledger import qualified Data.Aeson as Json import qualified Data.Aeson.Encoding as Json @@ -40,7 +36,7 @@ import qualified Data.Text as T import qualified Data.Text.Read as T type Metadata = - AlonzoTxAuxData ConwayEra + AlonzoTxAuxData DijkstraEra emptyMetadata :: Metadata emptyMetadata = @@ -68,7 +64,7 @@ metadataToText = metadataFromText :: Text -> Maybe Metadata metadataFromText txt = do bytes <- eitherToMaybe $ decodeBase16 (encodeUtf8 txt) - eitherToMaybe $ decodeCborAnn @ConwayEra "Metadata" decCBOR (toLazy bytes) + eitherToMaybe $ decodeCborAnn @DijkstraEra "Metadata" decCBOR (toLazy bytes) metadataToJson :: Metadata -> Json.Encoding metadataToJson (AlonzoTxAuxData labels _ _) = @@ -166,25 +162,30 @@ fromShelleyMetadata (ShelleyTxAuxData labels) = fromAllegraMetadata :: AllegraTxAuxData AllegraEra -> Metadata fromAllegraMetadata (AllegraTxAuxData labels timelocks) = - AlonzoTxAuxData labels (Ledger.Allegra.translateTimelock <$> timelocks) mempty + AlonzoTxAuxData labels (Ledger.Dijkstra.upgradeTimelock . Ledger.Allegra.translateTimelock <$> timelocks) mempty {-# INLINABLE fromAllegraMetadata #-} fromMaryMetadata :: AllegraTxAuxData MaryEra -> Metadata fromMaryMetadata (AllegraTxAuxData labels timelocks) = - AlonzoTxAuxData labels (Ledger.Allegra.translateTimelock <$> timelocks) mempty + AlonzoTxAuxData labels (Ledger.Dijkstra.upgradeTimelock . Ledger.Allegra.translateTimelock <$> timelocks) mempty {-# INLINABLE fromMaryMetadata #-} fromAlonzoMetadata :: AlonzoTxAuxData AlonzoEra -> Metadata -fromAlonzoMetadata = - Ledger.Alonzo.translateAlonzoTxAuxData +fromAlonzoMetadata (AlonzoTxAuxData labels timelocks plutus) = + AlonzoTxAuxData labels (Ledger.Dijkstra.upgradeTimelock . Ledger.Allegra.translateTimelock <$> timelocks) plutus {-# INLINABLE fromAlonzoMetadata #-} fromBabbageMetadata :: AlonzoTxAuxData BabbageEra -> Metadata -fromBabbageMetadata = - Ledger.upgradeTxAuxData +fromBabbageMetadata (AlonzoTxAuxData labels timelocks plutus) = + AlonzoTxAuxData labels (Ledger.Dijkstra.upgradeTimelock . Ledger.Allegra.translateTimelock <$> timelocks) plutus {-# INLINABLE fromBabbageMetadata #-} fromConwayMetadata :: AlonzoTxAuxData ConwayEra -> Metadata -fromConwayMetadata = - identity +fromConwayMetadata (AlonzoTxAuxData labels timelocks plutus) = + AlonzoTxAuxData labels (Ledger.Dijkstra.upgradeTimelock <$> timelocks) plutus {-# INLINABLE fromConwayMetadata #-} + +fromDijkstraMetadata :: AlonzoTxAuxData DijkstraEra -> Metadata +fromDijkstraMetadata = + identity +{-# INLINABLE fromDijkstraMetadata #-} diff --git a/src/Kupo/Data/Cardano/NativeScript.hs b/src/Kupo/Data/Cardano/NativeScript.hs index 76d9dca..3283f8b 100644 --- a/src/Kupo/Data/Cardano/NativeScript.hs +++ b/src/Kupo/Data/Cardano/NativeScript.hs @@ -18,6 +18,7 @@ import Cardano.Ledger.Keys ) import qualified Cardano.Ledger.Allegra.Scripts as Ledger.Allegra +import qualified Cardano.Ledger.Dijkstra.Scripts as Ledger.Dijkstra import qualified Cardano.Ledger.Shelley.Scripts as Ledger.Shelley -type NativeScript = Ledger.Allegra.Timelock ConwayEra +type NativeScript = Ledger.Dijkstra.DijkstraNativeScript DijkstraEra diff --git a/src/Kupo/Data/Cardano/Output.hs b/src/Kupo/Data/Cardano/Output.hs index c51bf07..ff92bea 100644 --- a/src/Kupo/Data/Cardano/Output.hs +++ b/src/Kupo/Data/Cardano/Output.hs @@ -17,8 +17,8 @@ import Kupo.Data.Cardano.Address ) import Kupo.Data.Cardano.Datum ( Datum - , fromConwayDatum - , toConwayDatum + , fromDijkstraDatum + , toDijkstraDatum ) import Kupo.Data.Cardano.Script ( ComparableScript @@ -53,7 +53,7 @@ import qualified Data.Map as Map -- Output type Output = - Ledger.Babbage.BabbageTxOut ConwayEra + Ledger.Babbage.BabbageTxOut DijkstraEra mkOutput :: Address @@ -65,7 +65,7 @@ mkOutput address value datum script = Ledger.Babbage.BabbageTxOut address value - (toConwayDatum datum) + (toDijkstraDatum datum) (maybeToStrictMaybe script) {-# INLINABLE mkOutput #-} @@ -116,9 +116,23 @@ fromBabbageOutput :: Ledger.Core.TxOut BabbageEra -> Output fromBabbageOutput = - Ledger.Core.upgradeTxOut + Ledger.Core.upgradeTxOut . Ledger.Core.upgradeTxOut {-# INLINABLE fromBabbageOutput #-} +fromConwayOutput + :: Ledger.Core.TxOut ConwayEra + -> Output +fromConwayOutput = + Ledger.Core.upgradeTxOut +{-# INLINABLE fromConwayOutput #-} + +fromDijkstraOutput + :: Ledger.Core.TxOut DijkstraEra + -> Output +fromDijkstraOutput = + identity +{-# INLINABLE fromDijkstraOutput #-} + getAddress :: Output -> Address @@ -137,7 +151,7 @@ getDatum :: Output -> Datum getDatum (Ledger.Babbage.BabbageTxOut _address _value datum _refScript) = - fromConwayDatum datum + fromDijkstraDatum datum {-# INLINABLE getDatum #-} getScript diff --git a/src/Kupo/Data/Cardano/Point.hs b/src/Kupo/Data/Cardano/Point.hs index 6c1ab35..599233a 100644 --- a/src/Kupo/Data/Cardano/Point.hs +++ b/src/Kupo/Data/Cardano/Point.hs @@ -45,7 +45,9 @@ import qualified Ouroboros.Network.Block as Ouroboros type Point = Ouroboros.Point Block -instance ToJSON Point where +-- ouroboros-network provides a general 'ToJSON (Ouroboros.Point block)' instance +-- that conflicts with this one, so we need OVERLAPPING to prefer this definition. +instance {-# OVERLAPPING #-} ToJSON Point where toJSON = error "ToJSON Point called instead of 'toEncoding'." toEncoding = pointToJson diff --git a/src/Kupo/Data/Cardano/Redeemers.hs b/src/Kupo/Data/Cardano/Redeemers.hs index 55d7080..fc985c7 100644 --- a/src/Kupo/Data/Cardano/Redeemers.hs +++ b/src/Kupo/Data/Cardano/Redeemers.hs @@ -7,6 +7,7 @@ import Kupo.Data.Cardano.BinaryData , fromAlonzoData , fromBabbageData , fromConwayData + , fromDijkstraData ) import Kupo.Data.Cardano.OutputIndex ( InputIndex @@ -15,12 +16,14 @@ import Kupo.Data.Cardano.OutputIndex import qualified Cardano.Ledger.Alonzo.Scripts as Ledger import qualified Cardano.Ledger.Alonzo.TxWits as Ledger import qualified Cardano.Ledger.Conway.Scripts as Ledger +import qualified Cardano.Ledger.Dijkstra.Scripts as Ledger import qualified Data.Map as Map data Redeemers = RedeemersAlonzo (Ledger.Redeemers AlonzoEra) | RedeemersBabbage (Ledger.Redeemers BabbageEra) | RedeemersConway (Ledger.Redeemers ConwayEra) + | RedeemersDisjkstra (Ledger.Redeemers DijkstraEra) lookupSpendRedeemer :: InputIndex @@ -36,3 +39,6 @@ lookupSpendRedeemer ix = \case RedeemersConway (Ledger.Redeemers redeemers) -> let purpose = Ledger.ConwaySpending (Ledger.AsIx (fromIntegral ix)) in fromConwayData . fst <$> Map.lookup purpose redeemers + RedeemersDisjkstra (Ledger.Redeemers redeemers) -> + let purpose = Ledger.DijkstraSpending (Ledger.AsIx (fromIntegral ix)) + in fromDijkstraData . fst <$> Map.lookup purpose redeemers diff --git a/src/Kupo/Data/Cardano/Script.hs b/src/Kupo/Data/Cardano/Script.hs index 6e18f46..e1580f3 100644 --- a/src/Kupo/Data/Cardano/Script.hs +++ b/src/Kupo/Data/Cardano/Script.hs @@ -16,10 +16,6 @@ import Kupo.Data.Cardano.NativeScript import Kupo.Data.Cardano.ScriptHash ( ScriptHash ) -import Ouroboros.Consensus.Util - ( eitherToMaybe - ) - import qualified Cardano.Ledger.Allegra.Scripts as Ledger.Allegra import qualified Cardano.Ledger.Allegra.TxAuxData as Ledger.Allegra import qualified Cardano.Ledger.Alonzo as Ledger.Alonzo @@ -27,6 +23,7 @@ import qualified Cardano.Ledger.Alonzo.Scripts as Ledger.Alonzo import qualified Cardano.Ledger.Alonzo.TxAuxData as Ledger.Alonzo import qualified Cardano.Ledger.Binary.Plain as Ledger import qualified Cardano.Ledger.Core as Ledger.Core +import qualified Cardano.Ledger.Dijkstra.Scripts as Ledger.Dijkstra import qualified Cardano.Ledger.Plutus.Language as Ledger import qualified Codec.CBOR.Decoding as Cbor @@ -37,14 +34,14 @@ import qualified Data.ByteString as BS import qualified Data.Map as Map type Script = - Ledger.Alonzo.Script ConwayEra + Ledger.Alonzo.Script DijkstraEra scriptFromAllegraAuxiliaryData :: forall era. ( Ledger.Core.Era era - , Ledger.Core.Script era ~ Ledger.Allegra.Timelock era + , Ledger.Core.NativeScript era ~ Ledger.Allegra.Timelock era ) - => (Ledger.Core.Script era -> Script) + => (Ledger.Allegra.Timelock era -> Script) -> Ledger.Allegra.AllegraTxAuxData era -> Map ScriptHash Script -> Map ScriptHash Script @@ -66,7 +63,7 @@ scriptFromAlonzoAuxiliaryData -> Map ScriptHash Script scriptFromAlonzoAuxiliaryData liftScript (Ledger.Alonzo.AlonzoTxAuxData _ scripts _) m0 = foldr - (\((liftScript . Ledger.Alonzo.TimelockScript) -> s) -> Map.insert (hashScript s) s) + (\((liftScript . Ledger.Alonzo.NativeScript) -> s) -> Map.insert (hashScript s) s) m0 scripts {-# INLINABLE scriptFromAlonzoAuxiliaryData #-} @@ -75,14 +72,14 @@ fromAllegraScript :: Ledger.Allegra.Timelock AllegraEra -> Script fromAllegraScript = - Ledger.Alonzo.TimelockScript . Ledger.Allegra.translateTimelock + Ledger.Alonzo.NativeScript . Ledger.Dijkstra.upgradeTimelock . Ledger.Allegra.translateTimelock {-# INLINABLE fromAllegraScript #-} fromMaryScript :: Ledger.Allegra.Timelock MaryEra -> Script fromMaryScript = - Ledger.Alonzo.TimelockScript . Ledger.Allegra.translateTimelock + Ledger.Alonzo.NativeScript . Ledger.Dijkstra.upgradeTimelock . Ledger.Allegra.translateTimelock {-# INLINABLE fromMaryScript #-} fromAlonzoScript @@ -96,29 +93,37 @@ fromBabbageScript :: Ledger.Alonzo.Script BabbageEra -> Script fromBabbageScript = - Ledger.Core.upgradeScript + Ledger.Core.upgradeScript . Ledger.Core.upgradeScript {-# INLINABLE fromBabbageScript #-} fromConwayScript :: Ledger.Alonzo.Script ConwayEra -> Script fromConwayScript = - identity + Ledger.Core.upgradeScript {-# INLINABLE fromConwayScript #-} +fromDijkstraScript + :: Ledger.Alonzo.Script DijkstraEra + -> Script +fromDijkstraScript = + identity +{-# INLINABLE fromDijkstraScript #-} + scriptToJson :: Script -> Json.Encoding scriptToJson script = encodeObject [ ("script", encodeBytes (Ledger.Core.originalBytes script)) , ("language", case script of - Ledger.Alonzo.TimelockScript _ -> + Ledger.Alonzo.NativeScript _ -> Json.text "native" Ledger.Alonzo.PlutusScript ps -> case Ledger.Alonzo.plutusScriptLanguage ps of Ledger.PlutusV1 -> Json.text "plutus:v1" Ledger.PlutusV2 -> Json.text "plutus:v2" Ledger.PlutusV3 -> Json.text "plutus:v3" + Ledger.PlutusV4 -> Json.text "plutus:v4" ) ] @@ -128,13 +133,14 @@ scriptToBytes scriptToBytes = let withTag n s = BS.singleton n <> Ledger.Core.originalBytes s in \case - Ledger.Alonzo.TimelockScript script -> + Ledger.Alonzo.NativeScript script -> withTag 0 script Ledger.Alonzo.PlutusScript script -> case Ledger.Alonzo.plutusScriptLanguage script of Ledger.PlutusV1 -> withTag 1 script Ledger.PlutusV2 -> withTag 2 script Ledger.PlutusV3 -> withTag 3 script + Ledger.PlutusV4 -> withTag 4 script unsafeScriptFromBytes :: HasCallStack @@ -150,12 +156,13 @@ scriptFromBytes scriptFromBytes (toLazy -> bytes) = eitherToMaybe $ do (script, tag) <- left (DecoderErrorDeserialiseFailure "Script") $ - Cbor.deserialiseFromBytes Cbor.decodeWord8 bytes + Cbor.deserialiseFromBytes Cbor.decodeWord bytes case tag of - 0 -> Ledger.Alonzo.TimelockScript <$> decodeCborAnn @BabbageEra "Timelock" decCBOR script + 0 -> Ledger.Alonzo.NativeScript <$> decodeCborAnn @DijkstraEra "DijkstraNativeScript" decCBOR script 1 -> plutusScript Ledger.PlutusV1 script 2 -> plutusScript Ledger.PlutusV2 script 3 -> plutusScript Ledger.PlutusV3 script + 4 -> plutusScript Ledger.PlutusV4 script t -> Left (DecoderErrorUnknownTag "Script" t) where plutusScript lang s = @@ -165,7 +172,7 @@ scriptFromBytes (toLazy -> bytes) = script = maybeToRight (Ledger.DecoderErrorCustom "Incompatible language and era" $ show (lang, uplc)) - (Ledger.Alonzo.mkBinaryPlutusScript @ConwayEra lang uplc) + (Ledger.Alonzo.mkBinaryPlutusScript lang uplc) in Ledger.Alonzo.PlutusScript <$> script @@ -173,14 +180,14 @@ fromNativeScript :: NativeScript -> Script fromNativeScript = - Ledger.Alonzo.TimelockScript + Ledger.Alonzo.NativeScript {-# INLINABLE fromNativeScript #-} hashScript :: Script -> ScriptHash hashScript = - Ledger.Core.hashScript @ConwayEra + Ledger.Core.hashScript @DijkstraEra {-# INLINABLE hashScript #-} newtype ComparableScript = ComparableScript { unComparableScript :: Script } diff --git a/src/Kupo/Data/Cardano/Transaction.hs b/src/Kupo/Data/Cardano/Transaction.hs index 134a3f4..cf88421 100644 --- a/src/Kupo/Data/Cardano/Transaction.hs +++ b/src/Kupo/Data/Cardano/Transaction.hs @@ -8,9 +8,7 @@ import Kupo.Data.Cardano.TransactionId ) import qualified Cardano.Chain.UTxO as Ledger.Byron -import qualified Cardano.Ledger.Alonzo.Tx as Ledger.Alonzo import qualified Cardano.Ledger.Core as Ledger -import qualified Cardano.Ledger.Shelley.Tx as Ledger.Shelley -- Transaction @@ -20,37 +18,35 @@ data Transaction !Ledger.Byron.Tx !Ledger.Byron.TxId | TransactionShelley - !(Ledger.Shelley.ShelleyTx ShelleyEra) + !(Ledger.Tx Ledger.TopTx ShelleyEra) | TransactionAllegra - !(Ledger.Shelley.ShelleyTx AllegraEra) + !(Ledger.Tx Ledger.TopTx AllegraEra) | TransactionMary - !(Ledger.Shelley.ShelleyTx MaryEra) + !(Ledger.Tx Ledger.TopTx MaryEra) | TransactionAlonzo - !(Ledger.Alonzo.AlonzoTx AlonzoEra) + !(Ledger.Tx Ledger.TopTx AlonzoEra) | TransactionBabbage - !(Ledger.Alonzo.AlonzoTx BabbageEra) + !(Ledger.Tx Ledger.TopTx BabbageEra) | TransactionConway - !(Ledger.Alonzo.AlonzoTx ConwayEra) + !(Ledger.Tx Ledger.TopTx ConwayEra) + | TransactionDijkstra + !(Ledger.Tx Ledger.TopTx DijkstraEra) instance HasTransactionId Transaction where getTransactionId = \case TransactionByron _ i -> transactionIdFromByron i TransactionShelley tx -> - let body = Ledger.Shelley.body tx - in Ledger.txIdTxBody @ShelleyEra body + Ledger.txIdTx @ShelleyEra tx TransactionAllegra tx -> - let body = Ledger.Shelley.body tx - in Ledger.txIdTxBody @AllegraEra body + Ledger.txIdTx @AllegraEra tx TransactionMary tx -> - let body = Ledger.Shelley.body tx - in Ledger.txIdTxBody @MaryEra body + Ledger.txIdTx @MaryEra tx TransactionAlonzo tx -> - let body = Ledger.Alonzo.body tx - in Ledger.txIdTxBody @AlonzoEra body + Ledger.txIdTx @AlonzoEra tx TransactionBabbage tx -> - let body = Ledger.Alonzo.body tx - in Ledger.txIdTxBody @BabbageEra body + Ledger.txIdTx @BabbageEra tx TransactionConway tx -> - let body = Ledger.Alonzo.body tx - in Ledger.txIdTxBody @ConwayEra body + Ledger.txIdTx @ConwayEra tx + TransactionDijkstra tx -> + Ledger.txIdTx @DijkstraEra tx diff --git a/src/Kupo/Data/Hydra.hs b/src/Kupo/Data/Hydra.hs index 01f0401..1bd18e2 100644 --- a/src/Kupo/Data/Hydra.hs +++ b/src/Kupo/Data/Hydra.hs @@ -10,8 +10,10 @@ import Cardano.Crypto.Hash , hashWith ) import Cardano.Ledger.Alonzo.Scripts - ( AlonzoPlutusPurpose (..) - , AsIx (..) + ( AsIx (..) + ) +import Cardano.Ledger.Conway.Scripts + ( ConwayPlutusPurpose (..) ) import Cardano.Ledger.Alonzo.TxWits ( unRedeemers @@ -26,6 +28,7 @@ import Cardano.Ledger.Api , scriptTxWitsL , witsTxL ) +import qualified Cardano.Ledger.Core as Ledger import Cardano.Ledger.Hashes ( unsafeMakeSafeHash ) @@ -47,9 +50,9 @@ import Kupo.Data.Cardano , TransactionId , Value , binaryDataFromBytes - , fromBabbageData - , fromBabbageOutput - , fromBabbageScript + , fromConwayData + , fromConwayOutput + , fromConwayScript , getOutputIndex , getTransactionId , mkOutput @@ -73,7 +76,6 @@ import Kupo.Data.PartialBlock , PartialTransaction (..) ) -import qualified Cardano.Ledger.Api as Ledger import qualified Codec.CBOR.Decoding as Cbor import qualified Codec.CBOR.Read as Cbor import qualified Data.Aeson.Key as Key @@ -181,7 +183,10 @@ decodePartialTransaction = Json.withObject "PartialTransaction" $ \o -> do bytes <- decodeBase16' hexText - tx <- case decodeCborAnn @ConwayEra "PartialTransaction" decCBOR (fromStrict bytes) of + -- NOTE: Hydra currently serialises transactions as Conway-era CBOR. When Hydra upgrades its + -- internal transaction encoding to Dijkstra, this line and the fromConway* calls below will + -- need to change. + (tx :: Ledger.Tx Ledger.TopTx ConwayEra) <- case decodeCborAnn @ConwayEra "PartialTransaction" decCBOR (fromStrict bytes) of Left e -> fail $ show e Right tx -> pure tx @@ -198,23 +203,23 @@ decodePartialTransaction = Json.withObject "PartialTransaction" $ \o -> do let body' = tx ^. bodyTxL let id = Ledger.txIdTxBody body' let wits' = tx ^. witsTxL - let outputs' = map fromBabbageOutput $ toList (body' ^. outputsTxBodyL) + let outputs' = toList (fromConwayOutput <$> body' ^. outputsTxBodyL) pure PartialTransaction { id , inputs = toList (body' ^. inputsTxBodyL) , outputs = withReferences 0 id outputs' - , datums = Map.map fromBabbageData $ unTxDats (wits' ^. datsTxWitsL) + , datums = Map.map fromConwayData $ unTxDats (wits' ^. datsTxWitsL) , spendRedeemers = Map.foldrWithKey (\purpose (redeemer, _) -> case purpose of - AlonzoSpending (AsIx ix) -> Map.insert (fromIntegral ix) (fromBabbageData redeemer) + ConwaySpending (AsIx ix) -> Map.insert (fromIntegral ix) (fromConwayData redeemer) _ -> identity ) mempty (unRedeemers $ wits' ^. rdmrsTxWitsL) - , scripts = Map.map fromBabbageScript (wits' ^. scriptTxWitsL) + , scripts = fromConwayScript <$> wits' ^. scriptTxWitsL , metadata = Nothing } @@ -305,6 +310,8 @@ decodeScriptInEnvelope = Json.withObject "ScriptInEnvelope" $ \o -> do scriptFromBytes' (BS.pack [2] <> nestedBytes) "PlutusScriptLanguage PlutusScriptV3" -> scriptFromBytes' (BS.pack [3] <> nestedBytes) + "PlutusScriptLanguage PlutusScriptV4" -> + scriptFromBytes' (BS.pack [4] <> nestedBytes) (_ :: Text) -> fail "unrecognized script language" where diff --git a/src/Kupo/Data/Ogmios.hs b/src/Kupo/Data/Ogmios.hs index 0b24ee2..5f172e4 100644 --- a/src/Kupo/Data/Ogmios.hs +++ b/src/Kupo/Data/Ogmios.hs @@ -325,6 +325,7 @@ decodeScript = Json.withObject "Script" $ \o -> do "plutus:v1" -> decodePlutus "01" =<< o .: "cbor" "plutus:v2" -> decodePlutus "02" =<< o .: "cbor" "plutus:v3" -> decodePlutus "03" =<< o .: "cbor" + "plutus:v4" -> decodePlutus "04" =<< o .: "cbor" (_ :: Text) -> fail "unrecognized script language" where decodeNative = diff --git a/src/Kupo/Prelude.hs b/src/Kupo/Prelude.hs index 6bd04ac..fc4f858 100644 --- a/src/Kupo/Prelude.hs +++ b/src/Kupo/Prelude.hs @@ -40,6 +40,7 @@ module Kupo.Prelude , at -- * Extras + , eitherToMaybe , foldrWithIndex , next , nubOn @@ -75,6 +76,7 @@ module Kupo.Prelude , AlonzoEra , BabbageEra , ConwayEra + , DijkstraEra , MostRecentEra -- * System @@ -92,7 +94,7 @@ import Cardano.Crypto.Hash , Hash (..) , HashAlgorithm (..) , hashFromBytes - , sizeHash + , hashSize ) import Cardano.Ledger.Allegra ( AllegraEra @@ -169,7 +171,8 @@ import Ouroboros.Consensus.Cardano.Block ( CardanoEras ) import Ouroboros.Consensus.Shelley.Eras - ( StandardCrypto + ( DijkstraEra + , StandardCrypto ) import Ouroboros.Consensus.Shelley.Ledger ( ShelleyBlock @@ -358,6 +361,10 @@ unsafeDecodeCbor lbl decoder = -- Extras -- +-- | Convert an 'Either' to a 'Maybe', discarding the error. +eitherToMaybe :: Either e a -> Maybe a +eitherToMaybe = either (const Nothing) Just + -- | Remove duplicates from a list based on information extracted from the -- elements. nubOn :: Eq b => (a -> b) -> [a] -> [a] @@ -419,7 +426,7 @@ unsafeHashFromBytes bytes digestSize :: forall alg. HashAlgorithm alg => Int digestSize = - fromIntegral (sizeHash (Proxy @alg)) + fromIntegral (hashSize (Proxy @alg)) {-# INLINABLE digestSize #-} hashToJson :: HashAlgorithm alg => Hash alg a -> Json.Encoding diff --git a/test/Test/Kupo/AppSpec.hs b/test/Test/Kupo/AppSpec.hs index 4e0bd35..7d645bd 100644 --- a/test/Test/Kupo/AppSpec.hs +++ b/test/Test/Kupo/AppSpec.hs @@ -37,7 +37,7 @@ import GHC.Generics ) import Kupo ( kupoWith - , newEnvironment + , newEnvironmentWith , runWith ) import Kupo.App @@ -146,6 +146,7 @@ import Network.HTTP.Client ( defaultManagerSettings , newManager ) +import qualified Network.Socket import Network.WebSockets ( ConnectionException (..) ) @@ -176,7 +177,6 @@ import Test.Kupo.Data.Generators ) import Test.QuickCheck ( Gen - , arbitrary , choose , counterexample , elements @@ -184,7 +184,6 @@ import Test.QuickCheck , frequency , label , oneof - , sized ) import Test.QuickCheck.Monadic ( assert @@ -193,7 +192,8 @@ import Test.QuickCheck.Monadic , run ) import Test.QuickCheck.Property - ( withMaxSuccess + ( ioProperty + , withMaxSuccess ) import Test.StateMachine ( CommandNames @@ -243,10 +243,10 @@ spec = do <$> runIO (lookupEnv varStateMachineIterations) prop "State-Machine" $ withMaxSuccess maxSuccess $ - forAll genInputManagement $ \inputManagement -> do - forAll genServerPort $ \serverPort -> do - let httpClient = newHttpClientWith manager (serverHost, serverPort) (\_ -> pure ()) - let stateMachine = StateMachine + forAll genInputManagement $ \inputManagement -> ioProperty $ do + serverPort <- getFreePort + let httpClient = newHttpClientWith manager (serverHost, serverPort) (\_ -> pure ()) + stateMachine = StateMachine initModel transition (precondition longestRollback) @@ -257,7 +257,7 @@ spec = do (semantics garbageCollectionInterval httpClient chan) mock (cleanup chan) - forAllCommands stateMachine Nothing $ \cmds -> monadicIO $ do + pure $ forAllCommands stateMachine Nothing $ \cmds -> monadicIO $ do let config = Configuration { chainProducer = CardanoNode -- NOTE: unused, but must be different than ReadOnlyReplica { nodeSocket = "/dev/null" @@ -275,7 +275,7 @@ spec = do , garbageCollectionInterval , deferIndexes } - env <- run (newEnvironment config) + env <- run (newEnvironmentWith throwIO config) producer <- run (newMockProducer httpClient <$> atomically (dupTChan chan)) fetchBlock <- run (newMockFetchBlock <$> atomically (dupTChan chan)) let fetchTip = throwIO UnusedFetchTipClient @@ -309,9 +309,12 @@ spec = do garbageCollectionInterval = 0.4 deferIndexes = InstallIndexesIfNotExist tracers = configureTracers (defaultTracers Nothing) nullTracer - genServerPort = sized $ \n -> do - i <- arbitrary - pure (1024 + n + i) + getFreePort = do + sock <- Network.Socket.socket Network.Socket.AF_INET Network.Socket.Stream 0 + Network.Socket.bind sock (Network.Socket.SockAddrInet 0 (Network.Socket.tupleToHostAddress (127,0,0,1))) + port <- Network.Socket.socketPort sock + Network.Socket.close sock + pure (fromIntegral port) -------------------------------------------------------------------------------- ---- Events / Respone