Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ledger presentation of multi-asset values directly. Lens to make this uniform over ShelleyBasedEra #360

Merged
merged 11 commits into from
Nov 10, 2023

Conversation

newhoggy
Copy link
Collaborator

@newhoggy newhoggy commented Nov 8, 2023

Changelog

- description: |
    Use ledger presentation of multi-asset values directly.  Lens to make this uniform over `ShelleyBasedEra`.
    
    Delete `ByronToAllegraEra`.
    
    New module `Cardano.Api.Ledger.Lens`.
    
    Modify `TxOutValue` to have `TxOutValueByron` and `TxOutValueShelleyBased` instead of `TxOutAdaOnly` and `TxOutValue` respectively.  `TxOutValueShelleyBased` now directly uses the ledger type instead of the `Value` type. 
    
    These functions have changed to either `L.Value (ShelleyLedgerEra era)` instead of `Value` or eons or both:
    - `genValue`
    - `genValueDefault`
    - `genValueForMinting`
# uncomment types applicable to the change:
  type:
  # - feature        # introduces a new feature
  - breaking       # the API has changed in a breaking way
  # - compatible     # the API has changed but is non-breaking
  # - optimisation   # measurable performance improvements
  # - improvement    # QoL changes e.g. refactoring
  # - bugfix         # fixes a defect
  # - test           # fixes/modifies tests
  # - maintenance    # not directly related to the code
  # - release        # related to a new release preparation
  # - documentation  # change in code docs, haddocks...

Context

This PR introduces adapter lenses adaAssetL and multiAssetL.

The former provides a uniform way to access the ADA asset in ledger values uniformly across all of ShelleyBasedEra which simplifies code considerably.

The latter provides an easy way to access all the non-ADA assets from Mary onwards.

The change also deletes ByronToAllegraEra

Further simplifications are possible but are left to future PRs to keep this PR small.

How to trust this PR

Highlight important bits of the PR that will make the review faster. If there are commands the reviewer can run to observe the new behavior, describe them.

Checklist

  • Commit sequence broadly makes sense and commits have useful messages
  • New tests are added if needed and existing tests are updated. See Running tests for more details
  • Self-reviewed the diff

{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}

module Cardano.Api.Eon.ByronToAllegraEra
Copy link
Collaborator Author

@newhoggy newhoggy Nov 8, 2023

Choose a reason for hiding this comment

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

This PR deletes ByronToAllegraEra because we don't need it anymore.

This moves us towards to goal of having fewer eons.

MaryEra -> r MaryEraOnwardsMary
AlonzoEra -> r MaryEraOnwardsAlonzo
BabbageEra -> r MaryEraOnwardsBabbage
ConwayEra -> r MaryEraOnwardsConway
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Deletes a case function because we don't need it anymore.

caseShelleyToAllegraOrMaryEraOnwards
adaAssetShelleyToAllegraEraL
adaAssetMaryEraOnwardsL
sbe
Copy link
Collaborator Author

@newhoggy newhoggy Nov 8, 2023

Choose a reason for hiding this comment

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

We use lenses to present a consistent interface across all Shelley based eras. With this single lens we can access the ADA value inside the L.Value ledgerera.

It is a direct field lookup, not a map lookup like selectLovelace in cardano-api.

Copy link
Contributor

Choose a reason for hiding this comment

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

We need to move this lens to cardano-ledger in a follow up PR.

)
=> ShelleyBasedEra era
-> L.Value (ShelleyLedgerEra era)
-> TxOutValue era
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This definition has two constructors like before, the main difference is that all Shelley eras are now treated the same and only Byron is different. This is as it should be because the ledger treats Byron as legacy so it doesn't even have lens support.

Copy link
Contributor

@Jimbo4350 Jimbo4350 Nov 9, 2023

Choose a reason for hiding this comment

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

The TxOutValueByron constructor will eventually be removed because:

makeByronTransactionBody :: ()
  => ByronEraOnly era
  -> TxBodyContent BuildTx era
  -> Either TxBodyError (TxBody era)

We don't need TxBodyContent BuildTx Era to construct a Byron tx as the majority of the fields aren't used in Byron. We are only interested in inputs, outputs and update proposals and we can parameterize makeByronTransactionBody as such.

We will follow the approach of ledger the ledger towards the Byron era as we don't want consumers of the api who are interested only in the latest shelley era dealing with Byron related types.

Most places where TxOutValueByron is called we see usage of the EmptyCase pragma either for the Byron era or the ShelleyBasedEra so the code is also hinting this to us.

TxOutAdaOnly _ l -> l
TxOutValue _ v -> selectLovelace v
TxOutValueByron _ l -> l
TxOutValueShelleyBased sbe v -> coinToLovelace $ v ^. A.adaAssetL sbe
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Example of where we use adaAssetL to access the ADA value.

& L.datumTxOutL .~ toBabbageTxOutDatum' txoutdata
& L.referenceScriptTxOutL .~ refScriptToShelleyScript cEra refScript
where
cEra = shelleyBasedToCardanoEra sbe
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Opportunity to simplify because L.mkBasicTxOut (toShelleyAddr addr) value is repeated. This wasn't the case previously.

lovelaceToCoin (Lovelace ll) = Shelley.Coin ll

coinToLovelace :: Shelley.Coin -> Lovelace
coinToLovelace (Shelley.Coin ll) = Lovelace ll
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We could delete Lovelace and use the Coin ledger type directly to avoid conversions.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should in another PR.

caseShelleyToAllegraOrMaryEraOnwards
(const (coinToValue v))
(const (fromMaryValue v))
sbe
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These conversions were functions were introduced to convert between Value and L.Value ledgerera. Value could actually be deleted if we use the ledger type directly everywhere and then these conversion functions go away.

…implify the code by deleting the feature we no longer use

toShelleyTxOut _ (TxOut addr (TxOutValue MaryEraOnwardsAlonzo value) txoutdata _) =
L.mkBasicTxOut (toShelleyAddr addr) (toMaryValue value)
& L.dataHashTxOutL .~ toAlonzoTxOutDataHash txoutdata
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This appears to be Alonzo only. Deleting this can simplify the code a lot.

@@ -223,6 +223,7 @@ library
-- exposed by cardano-api-ledger
Cardano.Api.Ledger

reexported-modules: Cardano.Api.Ledger.Lens
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

New export so we can use eon enabled from the CLI and from tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just expose it?

Copy link
Collaborator Author

@newhoggy newhoggy Nov 9, 2023

Choose a reason for hiding this comment

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

The code needs to live in the internal component because code in internal uses it. This doesn't apply to the other modules because those are never imported by code in internal.

@newhoggy newhoggy force-pushed the newhoggy/improved-TxOutValue-code branch from f27b67e to cf84f53 Compare November 9, 2023 06:51
@newhoggy newhoggy force-pushed the newhoggy/improved-TxOutValue-code branch from cf84f53 to 9774286 Compare November 9, 2023 06:53
-> StrictMaybe (L.DataHash StandardCrypto)
-> TxOutDatum ctx era
fromAlonzoTxOutDataHash _ SNothing = TxOutDatumNone
fromAlonzoTxOutDataHash s (SJust dh) = TxOutDatumHash s (ScriptDataHash dh)
Copy link
Collaborator Author

@newhoggy newhoggy Nov 9, 2023

Choose a reason for hiding this comment

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

A bunch of alonzo specific code disappears because we remove the feature that only worked in Alonzo and choose to no longer support.

Copy link
Contributor

@Jimbo4350 Jimbo4350 Nov 9, 2023

Choose a reason for hiding this comment

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

Datum hashes weren't deprecated. See in ledger: instance Crypto c => AlonzoEraTxOut (BabbageEra c) where. This is removing the possibility for users to specify datum hashes instead of inline datums!

SNothing -> TxOutDatumNone
SJust dh
| Just d <- Map.lookup dh txdatums -> TxOutDatumInTx' w (ScriptDataHash dh) (fromAlonzoData d)
| otherwise -> TxOutDatumHash w (ScriptDataHash dh)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A bunch of alonzo specific code disappears because we remove the feature that only worked in Alonzo and choose to no longer support.

toShelleyTxOutAny sbe (TxOut addr (TxOutValue MaryEraOnwardsBabbage value) txoutdata refScript) =
let cEra = shelleyBasedToCardanoEra sbe
in L.mkBasicTxOut (toShelleyAddr addr) (toMaryValue value)
& L.datumTxOutL .~ toBabbageTxOutDatum' txoutdata
Copy link
Collaborator Author

@newhoggy newhoggy Nov 9, 2023

Choose a reason for hiding this comment

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

Being able to remove this Alonzo specific one-liner allows toShelleyTxOutAny to be implemented with a single eon case.

lookupDRepDeposit
isRegPool
(toLedgerUTxO sbe utxo)
txbody
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Because the lens adapter makes the ADA asset interface consistent across all of ShelleyBasedEra we no longer need to case on the eon to implement evaluateTransactionBalance.

sbe

let addressInEra :: AddressInEra era
addressInEra = shelleyBasedEraConstraints sbe $ fromShelleyAddr sbe $ ledgerTxOut ^. L.addrTxOutL
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

More simplifications because the eon-enabled lens adapter makes ADA Asset appear uniform across all of ShelleyBasedEra.

@@ -170,6 +169,7 @@ library internal
, errors
, filepath
, formatting
, groups
Copy link
Collaborator Author

@newhoggy newhoggy Nov 9, 2023

Choose a reason for hiding this comment

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

We import groups because ledger implements Group for multi-asset values which we can use to negate the entire L.Value ledgerera.

@newhoggy newhoggy marked this pull request as ready for review November 9, 2023 07:04
@newhoggy newhoggy changed the title Use ledger presentation of multi-asset values Use ledger presentation of multi-asset values directly. Lens to make this uniform over ShelleyBasedEra Nov 9, 2023
caseShelleyToAllegraOrMaryEraOnwards
adaAssetShelleyToAllegraEraL
adaAssetMaryEraOnwardsL
sbe
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to move this lens to cardano-ledger in a follow up PR.

@@ -223,6 +223,7 @@ library
-- exposed by cardano-api-ledger
Cardano.Api.Ledger

reexported-modules: Cardano.Api.Ledger.Lens
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just expose it?

evalMultiAsset
sbe
shelleyBasedEraConstraints sbe
$ TxOutValueShelleyBased sbe
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@@ -828,21 +809,22 @@ makeTransactionBodyAutoBalance sbe systemstart history lpp@(LedgerProtocolParame
-- However, since at this point we know how much non-Ada change to give
-- we can use the true values for that.

let outgoingNonAda = mconcat [filterValue isNotAda v | (TxOut _ (TxOutValue _ v) _ _) <- txOuts txbodycontent]
let incomingNonAda = mconcat [filterValue isNotAda v | (TxOut _ (TxOutValue _ v) _ _) <- Map.elems $ unUTxO utxo]
let outgoingNonAda = mconcat [v | (TxOut _ (TxOutValueShelleyBased _ v) _ _) <- txOuts txbodycontent]
Copy link
Contributor

Choose a reason for hiding this comment

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

Where has the isNotAda filter gone?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In the old code, isNotAda removes the ADA asset, which is equivalent to setting the ADA asset value to zero. Later the ADA asset value is set by way of addition so the zero value is necessary:

lovelaceToValue (Lovelace (2^(64 :: Integer)) - 1) <> nonAdaChange

The new code doesn't both to set it to zero because instead of adding nonAdaChange to the ADA asset value we overwrite it with nonAdaChange instead:

change & A.adaAssetL sbe .~ lovelaceToCoin maxLovelaceChange

mkBasicTxOut sbe addr value =
shelleyBasedEraConstraints sbe $ L.mkBasicTxOut addr value

mkAdaValue :: ShelleyBasedEra era -> L.Coin -> L.Value (ShelleyLedgerEra era)
Copy link
Contributor

Choose a reason for hiding this comment

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

Another function that should live in the ledger

Copy link
Collaborator Author

@newhoggy newhoggy Nov 9, 2023

Choose a reason for hiding this comment

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

This can't move to the ledger as is because it used ShelleyBasedEra, but there is n eon-free version of this that can move to the ledger.

Copy link
Contributor

@Jimbo4350 Jimbo4350 Nov 9, 2023

Choose a reason for hiding this comment

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

Yes, ledger can implement a function that constructs a Value regardless of the era.

lovelaceToCoin (Lovelace ll) = Shelley.Coin ll

coinToLovelace :: Shelley.Coin -> Lovelace
coinToLovelace (Shelley.Coin ll) = Lovelace ll
Copy link
Contributor

Choose a reason for hiding this comment

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

We should in another PR.

cardano-api/internal/Cardano/Api/TxBody.hs Outdated Show resolved Hide resolved
-> StrictMaybe (L.DataHash StandardCrypto)
-> TxOutDatum ctx era
fromAlonzoTxOutDataHash _ SNothing = TxOutDatumNone
fromAlonzoTxOutDataHash s (SJust dh) = TxOutDatumHash s (ScriptDataHash dh)
Copy link
Contributor

@Jimbo4350 Jimbo4350 Nov 9, 2023

Choose a reason for hiding this comment

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

Datum hashes weren't deprecated. See in ledger: instance Crypto c => AlonzoEraTxOut (BabbageEra c) where. This is removing the possibility for users to specify datum hashes instead of inline datums!

toAlonzoTxOutDataHash' (TxOutDatumInTx' _ (ScriptDataHash dh) _) = SJust dh
toAlonzoTxOutDataHash' (TxOutDatumInline inlineDatumSupp _sd) =
case inlineDatumSupp :: BabbageEraOnwards AlonzoEra of {}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The code above gets the hash and the code below also gets the hash as a Datum.

@newhoggy newhoggy requested a review from Jimbo4350 November 9, 2023 15:18
@newhoggy newhoggy dismissed Jimbo4350’s stale review November 9, 2023 15:19

Comments addressed

, mkAdaValue

-- * Lenses
, strictMaybeL
, L.invalidBeforeL
Copy link
Contributor

@Jimbo4350 Jimbo4350 Nov 9, 2023

Choose a reason for hiding this comment

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

We need a clear distinction between ledger and cardano-api. We are mixing lenses here which is confusing.

Copy link
Contributor

@Jimbo4350 Jimbo4350 left a comment

Choose a reason for hiding this comment

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

Nice work. I would like to sort out Cardano.Api.Ledger and Cardano.Api.Ledger.Lens before we release again.

@newhoggy newhoggy added this pull request to the merge queue Nov 10, 2023
Merged via the queue into main with commit f39ae4d Nov 10, 2023
25 checks passed
@newhoggy newhoggy deleted the newhoggy/improved-TxOutValue-code branch November 10, 2023 00:54
newhoggy added a commit that referenced this pull request Mar 11, 2024
…es-for-key-commands

Command argument types for `key` commands
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.

3 participants