Skip to content

Conversation

@erikd
Copy link

@erikd erikd commented Sep 25, 2025

The fact that Hydra/Cardano.Api and other modules were re-exporting large chunks of cardano-api and cardano-ledger was causing significant problems when moving to a more recent version of cardano-api.

Re-exporting things from one module to another in the same package is never a problem.

Re-exporting simple basic type (eg ByteString or Text` is rarely a problem.

Re-export whole modules from outside the current package is a really bad idea, especially where typeclass instances are involved. While Haskell provides good tools to control the visibility of types and functions, there is almost zero control of the visibility of typeclass instance. There is no way to stop them being exported from or imported into a module. If two packages both export an instance for a specific type and modules need to be imported from both packages, there is no way to control which instance is visible.

There are a handful of TODOs that can probably only be addressed after the removal of re-exports is complete.

  • CHANGELOG updated or not needed
  • Documentation updated or not needed
  • Haddocks updated or not needed
  • No new TODOs introduced or explained herafter

@erikd erikd marked this pull request as draft September 25, 2025 09:30
@github-actions
Copy link

github-actions bot commented Sep 25, 2025

Transaction costs

Sizes and execution budgets for Hydra protocol transactions. Note that unlisted parameters are currently using arbitrary values and results are not fully deterministic and comparable to previous runs.

Metadata
Generated at 2025-09-26 10:59:11.796634327 UTC
Max. memory units 14000000
Max. CPU units 10000000000
Max. tx size (kB) 16384

Script summary

Name Hash Size (Bytes)
νInitial c8a101a5c8ac4816b0dceb59ce31fc2258e387de828f02961d2f2045 2652
νCommit 61458bc2f297fff3cc5df6ac7ab57cefd87763b0b7bd722146a1035c 685
νHead a1442faf26d4ec409e2f62a685c1d4893f8d6bcbaf7bcb59d6fa1340 14599
μHead fd173b993e12103cd734ca6710d364e17120a5eb37a224c64ab2b188* 5284
νDeposit ae01dade3a9c346d5c93ae3ce339412b90a0b8f83f94ec6baa24e30c 1102
  • The minting policy hash is only usable for comparison. As the script is parameterized, the actual script is unique per head.

Init transaction costs

Parties Tx size % max Mem % max CPU Min fee ₳
1 5836 10.59 3.36 0.52
2 6037 12.25 3.87 0.54
3 6238 14.48 4.58 0.57
5 6641 18.43 5.81 0.63
10 7647 28.90 9.10 0.79
43 14279 98.58 30.79 1.80

Commit transaction costs

This uses ada-only outputs for better comparability.

UTxO Tx size % max Mem % max CPU Min fee ₳
1 556 2.44 1.16 0.20
2 743 3.38 1.73 0.22
3 922 4.36 2.33 0.24
5 1283 6.41 3.60 0.28
10 2176 12.13 7.25 0.40
54 10072 98.61 68.52 1.88

CollectCom transaction costs

Parties UTxO (bytes) Tx size % max Mem % max CPU Min fee ₳
1 57 525 24.42 7.12 0.42
2 113 636 32.24 9.37 0.51
3 170 747 41.46 11.98 0.60
4 226 862 53.70 15.30 0.73
5 282 969 64.26 18.20 0.84
6 338 1081 63.93 18.54 0.85
7 393 1192 86.99 24.54 1.08
8 451 1303 80.12 23.15 1.02

Cost of Increment Transaction

Parties Tx size % max Mem % max CPU Min fee ₳
1 1794 24.37 7.71 0.48
2 2003 26.87 9.07 0.52
3 2060 26.86 9.75 0.53
5 2448 32.33 12.60 0.61
10 3285 43.71 19.13 0.79
39 7635 98.82 53.75 1.67

Cost of Decrement Transaction

Parties Tx size % max Mem % max CPU Min fee ₳
1 626 22.80 7.39 0.42
2 703 22.58 7.95 0.42
3 962 26.61 9.77 0.48
5 1145 28.07 11.48 0.51
10 2009 38.31 17.69 0.68
41 6737 99.67 55.42 1.65

Close transaction costs

Parties Tx size % max Mem % max CPU Min fee ₳
1 657 29.09 8.89 0.48
2 838 31.69 10.29 0.52
3 955 30.98 10.76 0.52
5 1282 37.73 13.99 0.61
10 2199 46.27 19.80 0.76
36 6000 97.61 51.49 1.57

Contest transaction costs

Parties Tx size % max Mem % max CPU Min fee ₳
1 679 33.83 10.16 0.53
2 874 36.52 11.59 0.57
3 899 37.20 12.40 0.58
5 1274 42.53 15.25 0.66
10 2058 54.35 21.90 0.84
28 4899 97.34 45.88 1.48

Abort transaction costs

There is some variation due to the random mixture of initial and already committed outputs.

Parties Tx size % max Mem % max CPU Min fee ₳
1 5810 27.08 9.08 0.69
2 5920 34.75 11.63 0.78
3 5903 37.12 12.31 0.80
4 6359 55.85 18.86 1.02
5 6530 63.85 21.63 1.11
6 6662 71.82 24.22 1.20
7 6762 83.47 28.18 1.33
8 6894 92.40 31.06 1.42

FanOut transaction costs

Involves spending head output and burning head tokens. Uses ada-only UTXO for better comparability.

Parties UTxO UTxO (bytes) Tx size % max Mem % max CPU Min fee ₳
10 0 0 5835 18.93 6.32 0.61
10 5 285 6004 30.16 10.71 0.74
10 10 568 6173 39.69 14.52 0.85
10 20 1141 6515 60.42 22.68 1.09
10 30 1709 6855 80.04 30.46 1.32
10 39 2222 7162 98.49 37.73 1.53

End-to-end benchmark results

This page is intended to collect the latest end-to-end benchmark results produced by Hydra's continuous integration (CI) system from the latest master code.

Please note that these results are approximate as they are currently produced from limited cloud VMs and not controlled hardware. Rather than focusing on the absolute results, the emphasis should be on relative results, such as how the timings for a scenario evolve as the code changes.

Generated at 2025-09-26 11:02:23.63790435 UTC

Baseline Scenario

Number of nodes 1
Number of txs 300
Avg. Confirmation Time (ms) 5.251245556
P99 7.787443809999988ms
P95 6.4171106ms
P50 5.0353455ms
Number of Invalid txs 0

Memory data

Time Used Free
2025-09-26 11:01:03.286889332 UTC 1317M 6867M
2025-09-26 11:01:04.270766457 UTC 1361M 6787M
2025-09-26 11:01:05.270753831 UTC 1430M 6645M
2025-09-26 11:01:06.270861161 UTC 1452M 6622M
2025-09-26 11:01:07.270730767 UTC 1456M 6615M
2025-09-26 11:01:08.270854495 UTC 1462M 6604M
2025-09-26 11:01:09.270863389 UTC 1462M 6604M
2025-09-26 11:01:10.270846668 UTC 1462M 6604M
2025-09-26 11:01:11.270810545 UTC 1462M 6604M
2025-09-26 11:01:12.27078601 UTC 1466M 6600M
2025-09-26 11:01:13.270801446 UTC 1466M 6600M
2025-09-26 11:01:14.270857619 UTC 1466M 6600M
2025-09-26 11:01:15.270757423 UTC 1466M 6600M
2025-09-26 11:01:16.270867553 UTC 1466M 6600M
2025-09-26 11:01:17.270878642 UTC 1466M 6600M
2025-09-26 11:01:18.270845021 UTC 1466M 6600M
2025-09-26 11:01:19.270855892 UTC 1466M 6600M
2025-09-26 11:01:20.270760094 UTC 1466M 6600M
2025-09-26 11:01:21.270849933 UTC 1466M 6599M
2025-09-26 11:01:22.270887251 UTC 1466M 6599M
2025-09-26 11:01:23.270811459 UTC 1466M 6599M
2025-09-26 11:01:24.270865855 UTC 1466M 6599M
2025-09-26 11:01:25.270771771 UTC 1466M 6599M
2025-09-26 11:01:26.270854813 UTC 1466M 6598M
2025-09-26 11:01:27.270842962 UTC 1466M 6598M
2025-09-26 11:01:28.270847384 UTC 1468M 6596M
2025-09-26 11:01:29.270775255 UTC 1468M 6596M
2025-09-26 11:01:30.270723689 UTC 1469M 6596M

Three local nodes

Number of nodes 3
Number of txs 900
Avg. Confirmation Time (ms) 40.203484143
P99 67.16986677999999ms
P95 58.02877994999999ms
P50 39.0445265ms
Number of Invalid txs 0

Memory data

Time Used Free
2025-09-26 11:01:41.70113294 UTC 1366M 6735M
2025-09-26 11:01:42.701121048 UTC 1366M 6735M
2025-09-26 11:01:43.701025807 UTC 1381M 6720M
2025-09-26 11:01:44.70112016 UTC 1555M 6467M
2025-09-26 11:01:45.701094862 UTC 1567M 6454M
2025-09-26 11:01:46.702011019 UTC 1597M 6424M
2025-09-26 11:01:47.702616369 UTC 1627M 6389M
2025-09-26 11:01:48.701906479 UTC 1653M 6353M
2025-09-26 11:01:49.704228284 UTC 1672M 6324M
2025-09-26 11:01:50.702509567 UTC 1683M 6302M
2025-09-26 11:01:51.702123426 UTC 1687M 6289M
2025-09-26 11:01:52.70253053 UTC 1688M 6281M
2025-09-26 11:01:53.701942327 UTC 1705M 6257M
2025-09-26 11:01:54.704955742 UTC 1714M 6241M
2025-09-26 11:01:55.701709691 UTC 1730M 6218M
2025-09-26 11:01:56.702655284 UTC 1734M 6206M
2025-09-26 11:01:57.702110778 UTC 1740M 6193M
2025-09-26 11:01:58.701070943 UTC 1747M 6178M
2025-09-26 11:01:59.70114752 UTC 1768M 6150M
2025-09-26 11:02:00.7010623 UTC 1768M 6149M
2025-09-26 11:02:01.701021526 UTC 1768M 6149M
2025-09-26 11:02:02.701053661 UTC 1768M 6149M
2025-09-26 11:02:03.701045735 UTC 1768M 6149M
2025-09-26 11:02:04.701141372 UTC 1768M 6149M
2025-09-26 11:02:05.701079454 UTC 1768M 6149M
2025-09-26 11:02:06.701179164 UTC 1767M 6149M
2025-09-26 11:02:07.701192415 UTC 1768M 6148M
2025-09-26 11:02:08.701052029 UTC 1768M 6148M
2025-09-26 11:02:09.701069889 UTC 1768M 6148M
2025-09-26 11:02:10.701060835 UTC 1768M 6148M
2025-09-26 11:02:11.701064589 UTC 1767M 6148M
2025-09-26 11:02:12.701119786 UTC 1767M 6148M
2025-09-26 11:02:13.701183639 UTC 1767M 6148M
2025-09-26 11:02:14.701065177 UTC 1767M 6148M
2025-09-26 11:02:15.701112894 UTC 1767M 6148M
2025-09-26 11:02:16.701167229 UTC 1767M 6148M
2025-09-26 11:02:17.701189981 UTC 1767M 6148M
2025-09-26 11:02:18.701061769 UTC 1767M 6148M
2025-09-26 11:02:19.701101722 UTC 1766M 6148M
2025-09-26 11:02:20.701088095 UTC 1766M 6147M
2025-09-26 11:02:21.701071864 UTC 1771M 6142M
2025-09-26 11:02:22.701110393 UTC 1771M 6142M

@github-actions
Copy link

Transaction cost differences

No cost or size differences found

@erikd erikd force-pushed the erikd/rm-re-exports branch 4 times, most recently from e9389c4 to c49939f Compare September 26, 2025 04:47
@erikd erikd marked this pull request as ready for review September 26, 2025 05:11
@erikd erikd force-pushed the erikd/rm-re-exports branch from c49939f to cf713c1 Compare September 26, 2025 10:54
Copy link
Member

@ch1bo ch1bo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand what specifically caused issues. Can you point me to 1-2 examples changes in cardano-api that are problematic for hydra-cardano-api (and its usage)?

, blockfrost-client >=0.9.2.0
, cardano-api
, http-conduit
, hydra-cardano-api
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole intention of hydra-cardano-api is to insulate this (and other packages) from changes in cardano-api. By depending on both, the purpose of depending on hydra-cardano-api is moot and we should switch in full.

Copy link
Author

@erikd erikd Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea of hydra-cardano-api just indiscriminately re-exporting large piles of stuff from cardano-api and cardano-ledger is highly flawed. I would also predict that it would cause more problems in the long term than whatever convenience it provides in the short term

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ch1bo We should switch in full and upstream what we can.

The one thing that this library does do that can be considered an end-user assurance are the invariants forall era. era ~ ConwayEra and forall plutusVersion. plutusVersion ~ PlutusV3.

This claim is fine, and a library that is scoped to that guarantee is not objectionable. (And that work is here https://github.com/cardano-scaling/cardano-api-latest) I don't mind either way on that issue.

What is not helping us here is arbitrarily passthrough of these modules, especially as some of these things contain type class instances, and others come from interfaces that are intended to be used qualified. Cardano.Api.UTxO is upstreamed now and there are also 6 or so other user-facing modules in cardano-api now besides the main one Cardano.Api that I think would also benefit from being imported qualified. Transitively importing those is just going to create collisions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you say we should depend on cardano-api-latest only? I'm fine with anything and just want to point out that depending on both cardano-api and hydra-cardano-api is not a good idea (because this then makes it even further unclear where things come from).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still import cardano-api as cardano-api-latest would only reexport synonyms and wouldn't re-export Cardano.Api.UTxO for example. Qualification could be good here i.e Latest.Era Latest.TxBody etc.

genericCoverTable,
pickBlind,
module Test.Hspec,
module Test.Hspec.QuickCheck,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Test.Hydra.Prelude is following ADR8 to shorten import lists (of test modules in this case). For example on this diff are 101 mentions of "import Test.Hspec", mostly about added imports. Whats the rationale for this change? How are you suggesting to change ADR8?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@locallycompact would be looking for your thoughts here :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed those re-exports as a test case for the removal of large and more difficult re-exports. I wanted to see how easy it was to remove those particular re-exports from this code base. I wanted to start small before I went on to bigger things.

I think they should be removed. I am not going to die on that hill.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think all re-exports should be removed, preludes included. I will refer to all things of this sort as "implicit imports", so this logic applies to both the prelude and hydra-cardano-api both.

I would supercede ADR8 in the following way, briefly:

Implicit imports compete for space in the developer's working memory. A new contributor who did not write the prelude will not be aware of what is re-exported there, and will instinctively use the familiar option e.g BS.writeFile as opposed to writeFileBS.

Implicit imports compete with -Wmissing-import-lists, which is a useful forensic tool in Haskell for identifying the ultimate source of a symbol and controlling it's scope. -Wmissing-import-lists is activated by default in hydra-coding-standards-0.7.0 because it is very helpful for gaining familiarity with new code.

Implicit imports also obscure when a library can be easily mutualised. If we end up with, as we have in the past, collections of related functionality that can be upstreamed to an library that is not strictly dependent on hydra, then we gain the benefit of mutual interest from contributors in that functionality specifically. When we have implicit symbols it is not obvious what libraries would actually be needed to execute that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then propose a change or supersede ADR8 along with the change in the code how it would turn out.

Not sure what your proposal would say about NoImplicitPrelude which is currently set for all our packages and the rationale which Prelude is acceptable / what makes base better than relude, cardano-prelude, hydra-prelude... or whether you suggest to import everything explicit always? I'm personally a big fan of explicit imports, but hundreds of lines of Data.Eq, Text.Show, GHC.Generics, Control.Monad, etc. not so much anymore.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sympathic to NoImplicitPrelude for really standard Preludes like relude and RIO etc and for the Control.Monad spam, and it has been mentioned to the GHC developers in the past that there should be a way to make this one principled exception for -Wmissing-import-lists. I could check on the status of that. My preference is still to yield to -Wmissing-import-lists.

@erikd
Copy link
Author

erikd commented Sep 28, 2025

I'm not sure I understand what specifically caused issues. Can you point me to 1-2 examples changes in cardano-api that are problematic for hydra-cardano-api (and its usage)?

On branch erikd/capi-10.17 commit b37c6f673761ce92ee72ff830da321e91acccb48 we get :


testlib/Test/Hydra/Tx/Gen.hs:339:32: error: [GHC-43085]
    • Overlapping instances for Arbitrary
                                  (cardano-ledger-core-1.17.0.0:Cardano.Ledger.Core.PParams.PParamsUpdate
                                     cardano-ledger-conway-1.19.0.0:Cardano.Ledger.Conway.Era.ConwayEra)
        arising from a use of ‘arbitrary’
      two instances involving out-of-scope types
        Matching instances:
          instance (cardano-ledger-core-1.17.0.0:Cardano.Ledger.Core.Era.Era
                      era,
                    Arbitrary
                      (cardano-ledger-core-1.17.0.0:Cardano.Ledger.Core.PParams.PParamsHKD
                         Ledger.StrictMaybe era)) =>
                   Arbitrary
                     (cardano-ledger-core-1.17.0.0:Cardano.Ledger.Core.PParams.PParamsUpdate
                        era)
          instance (cardano-ledger-core-1.17.0.0:Cardano.Ledger.Core.Era.Era
                      era,
                    Arbitrary
                      (cardano-ledger-core-1.17.0.0:Cardano.Ledger.Core.PParams.PParamsHKD
                         Ledger.StrictMaybe era)) =>
                   Arbitrary
                     (cardano-ledger-core-1.17.0.0:Cardano.Ledger.Core.PParams.PParamsUpdate
                        era)
    • In the second argument of ‘(<$>)’, namely ‘arbitrary’
      In the expression: fromLedgerTx <$> arbitrary
      In an equation for ‘arbitrary’:
          arbitrary = fromLedgerTx <$> arbitrary
    |
339 |   arbitrary = fromLedgerTx <$> arbitrary
    |                                ^^^^

GHC gives adequate explanation of which type has and overlapping typeclass instance, but nothing about where those typeclass instances are defined. Due to the heavy resuse of re=exporting of modules external to this package, figuring out where the overlapping instances are coming from is rather difficult. Since the instances are defined elsewhere (probably cardano-api and cardano-ledger) things like the OVERLAPABLE and OVERLAPPING language pragmas cannot help here.

Re-exporting with a package is always fine, it helps define the API for that package, Re-exporting types and functions, even from outside the package is usually fine. Re-exporting whole modules from external packages is will include re-exporting type class instances. Haskell provides very good techniques for controlling the importing of types and functions (eg explicit and qualitifed imports) but very poor control over importing typoclass instances.

Re-exporting of for external packages is usually considered a bad idea.

@locallycompact
Copy link
Contributor

We really have to get rid of this as early as possible. This is a complete nightmare to deal with. I am currently dealing with dozens of errors like this.

src/Hydra/Chain/Blockfrost/Client.hs:196:52: error:
    Ambiguous occurrence 'CostModels'
    It could refer to
       either 'Cardano.Ledger.Plutus.CostModels.CostModels',
              imported from 'Cardano.Ledger.Plutus.CostModels' at src/Hydra/Chain/Blockfrost/Client.hs:56:42-51
           or 'Hydra.Cardano.Api.CostModels',
              imported from 'Hydra.Cardano.Api' at src/Hydra/Chain/Blockfrost/Client.hs:36:1-87
              (and originally defined in 'Cardano.Api.ProtocolParameters')
    |
196 |   convertCostModels :: Blockfrost.CostModelsRaw -> CostModels

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants