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 Cardano API function calculateMinimumUTxO for minimum UTxO calculations. #3368

Merged
merged 36 commits into from
Jul 13, 2022

Conversation

jonathanknowles
Copy link
Member

@jonathanknowles jonathanknowles commented Jun 30, 2022

Issue Number

ADP-1978

Summary

This PR uses the Cardano API function calculateMinimumUTxO to replace our hand-coded computeMinimumAdaQuantity function.

Goals

  1. Ensure that we cannot underestimate minimum UTxO values.
    It's imperative that we do not underestimate minimum UTxO quantities, as this may result in the creation of transactions that are unacceptable to the ledger. In the case of change generation, this would be particularly problematic, as change outputs are generated automatically, and users do not have direct control over the ada quantities generated.

  2. Decrease the complexity and fragility of minimum UTxO calculations within the wallet.
    The current design makes it trivial to specify a minimum UTxO function that's inappropriate for the era in which a transaction will be submitted. Using the wrong minimum UTxO function makes it possible to create transactions that will ultimately be rejected by the ledger.

Design

We replace the existing MinimumUTxOValue type with the following pair of types:

data MinimumUTxO where
    MinimumUTxONone
        :: MinimumUTxO
    MinimumUTxOConstant
        :: Coin
        -> MinimumUTxO
    MinimumUTxOForShelleyBasedEraOf
        :: MinimumUTxOForShelleyBasedEra
        -> MinimumUTxO

data MinimumUTxOForShelleyBasedEra where
    MinimumUTxOForShelleyBasedEra
        :: ShelleyBasedEra era
        -> PParams (ShelleyLedgerEra era)
        -> MinimumUTxOForShelleyBasedEra

Where:

  • MinimumUTxONone
    Specifies that there is no minimum UTxO value.
  • MinimumUTxOConstant
    Specifies a constant minimum UTxO value.
  • MinimumUTxOForShelleyBasedEra
    Represents a minimum UTxO function for a Shelley-based era. Ensures that values can only be constructed with a set of protocol parameters that are specific to the given era.

Furthermore, we make the following change to Primitive.Types.ProtocolParameters:

-    , minimumUTxOvalue
-        :: MinimumUTxOValue
-        -- ^ The minimum UTxO value.
+    , minimumUTxO
+        :: MinimumUTxO
+        -- ^ Represents a way of calculating minimum UTxO values.

Finally, we introduce function computeMinimumCoinForUTxO:

computeMinimumCoinForUTxO :: MinimumUTxO -> TokenMap -> Coin

This function computes a minimum Coinvalue for a TokenMap that is destined for inclusion in a transaction output.

We use the above function to re-implement Shelley.Transaction.constraints.txOutputMinimumAdaQuantity, which means that both the coin selection and migration algorithms will take advantage of this updated implementation.

Completed QA Tasks

  • Transfer our existing test coverage of computeMinimumAdaQuantity (including golden tests) to computeMinimumCoinForUTxO.
  • Verify that our minimum UTxO calculation is stable w.r.t. to the starting ada quantity. In particular, it should obey the following property:
  • Determine what level of test coverage is applied to the Cardano API function calculateMinimumUTxO. Ensure that we build appropriate test coverage to cover any deficits.
  • Ensure that we have test coverage to cover all valid lengths of addresses.
  • Ensure that we have test coverage to characterize the effect of different coin sizes (up to the limit of txOutMaxCoin).
  • Ensure that we have test coverage to characterize the effect of different token quantities (up to the limit of txOutMaxTokenQuantity).

Completed Implementation Tasks

  • Determine the most appropriate way to initialize field minimumUTxO within function fromBlockfrostPP. See 3c03560 and ADP-1994.
  • Remove type MinimumUTxOValue from Primitive.Types.
  • Remove function computeMinimumAdaQuantity and associated tests.

Completed Admin Tasks

  • Create a ticket to review and revise the behaviour of the Blockfrost protocol parameter conversion function so that it does not hard-code the choice of minimum UTxO function to the Alonzo era (which uses a coins-per-word calculation). See ADP-1994.
  • Create an issue on cardano-node to report the fact that the calculateMinimumUTxO function does not appear to reach a fixed point before returning its result. (See [QUESTION] - Using the Cardano API to compute minimum UTxO values. IntersectMBO/cardano-node#4163). The current behaviour appears to be as follows:
    • If we call calculateMinimumUTxO with Coin 0 and get Coin a back.
    • If we call calculateMinimumUTxO with Coin a and get Coin b back.
    • It's possible for b to be greater than a.

@jonathanknowles jonathanknowles force-pushed the jonathanknowles/use-cardano-api-calculateMinimumUTxO branch 3 times, most recently from b0411ee to c27ec5d Compare July 1, 2022 02:50
@jonathanknowles jonathanknowles changed the title WIP: Use Cardano API function calculateMinimumUTxO to replace hand-coded function. WIP: Use Cardano API function calculateMinimumUTxO to replace hand-coded calculation. Jul 1, 2022
@jonathanknowles jonathanknowles force-pushed the jonathanknowles/use-cardano-api-calculateMinimumUTxO branch 2 times, most recently from 3519711 to 0d941e3 Compare July 1, 2022 06:25
@jonathanknowles jonathanknowles changed the title WIP: Use Cardano API function calculateMinimumUTxO to replace hand-coded calculation. WIP: Use Cardano API function calculateMinimumUTxO for more reliable minimum UTxO calculations. Jul 1, 2022
@jonathanknowles jonathanknowles self-assigned this Jul 1, 2022
@jonathanknowles jonathanknowles changed the title WIP: Use Cardano API function calculateMinimumUTxO for more reliable minimum UTxO calculations. WIP: Use Cardano API function calculateMinimumUTxO for minimum UTxO calculations. Jul 1, 2022
@jonathanknowles jonathanknowles force-pushed the jonathanknowles/use-cardano-api-calculateMinimumUTxO branch 3 times, most recently from c31c29d to b450e20 Compare July 4, 2022 06:13
@jonathanknowles jonathanknowles force-pushed the jonathanknowles/use-cardano-api-calculateMinimumUTxO branch 3 times, most recently from a2a2f7e to 9b0a7a6 Compare July 12, 2022 00:49
jonathanknowles added a commit that referenced this pull request Jul 12, 2022
@jonathanknowles jonathanknowles changed the title WIP: Use Cardano API function calculateMinimumUTxO for minimum UTxO calculations. Use Cardano API function calculateMinimumUTxO for minimum UTxO calculations. Jul 12, 2022
@jonathanknowles jonathanknowles marked this pull request as ready for review July 12, 2022 01:53
This allows `verify` to modify any `Testable` value.
This function compares the stability of:

- the Cardano API function 'calculateMinimumUTxO'
- the wallet function 'computeMinimumCoinForUTxO'
…nds`.

Simplify this property by moving the error pattern match to inner
function `apiComputeMinCoin`. We assume that error pattern will never
match. But if it does, then this property will still fail.
We add golden minimum UTxO values for all Shelley-based eras.
This extension was only used once in the whole module.

In the particular case it was used, it is arguably clearer and safer to
state the era explicitly.
In particular, we indicate more clearly which value was expected, and
which value was returned.

For example:

```
Failures:

  lib/shelley/test/unit/Cardano/Wallet/Shelley/MinimumUTxOSpec.hs:490:9:
  1) computeMinimumCoinForUTxO, Golden Tests, goldenTests_computeMinimumCoinForUTxO Babbage, golden test #3
       Falsified (after 1 test):
         resultExpected:                                                                                                                                                                           Coin 1323170
             Coin 1323170

         resultReturned:
             Coin 1357650

         Condition violated: resultReturned == resultExpected
```
…bility`.

We further explain the purpose of this property.

If the Cardano API function `calculateMinimumUTxO` is ever changed so
that it computes a fixed point before returning its result, this
property will fail.

However, this is valuable information, as it tells us that we might be
able to revise our implementation of `computeMinimumCoinForUTxO` to
reduce the level of overestimation.

In response to review feedback.
….Gen`.

This commit introduces generator function `genLedgerCoinOfSimilarMagnitude`,
which, when given a coin value, will generate another coin value that is
of a similar magnitude.

When generating test values of `MinimumUTxO`, we apply the generator
function `genLedgerCoinOfSimilarMagnitude` to protocol parameter values
obtained from real mainnet genesis files. This enables us to generate
more realistic values of `MinimumUTxO`.

In response to review feedback:
#3368 (comment)
…Spec`.

Since we've already defined a set of test protocol parameter values
within `MinimumUTxO.Gen`, we might as well re-use these test parameter
values within `MinimumUTxOSpec`. This reduces the amount of repetition.
@jonathanknowles jonathanknowles force-pushed the jonathanknowles/use-cardano-api-calculateMinimumUTxO branch from a347f34 to f08e0a0 Compare July 13, 2022 04:42
@jonathanknowles
Copy link
Member Author

bors r+

iohk-bors bot added a commit that referenced this pull request Jul 13, 2022
3368: Use Cardano API function `calculateMinimumUTxO` for minimum UTxO calculations. r=jonathanknowles a=jonathanknowles

## Issue Number 

ADP-1978

## Summary

This PR uses the Cardano API function [`calculateMinimumUTxO`](https://github.com/input-output-hk/cardano-node/blob/42809ad3d420f0695eb147d4c66b90573d27cafd/cardano-api/src/Cardano/Api/Fees.hs#L1226) to replace our hand-coded [`computeMinimumAdaQuantity`](https://github.com/input-output-hk/cardano-wallet/blob/4f0b2f3c3cb9af139029418f4a2ebeb166fd0691/lib/shelley/src/Cardano/Wallet/Shelley/Compatibility/Ledger.hs#L121) function.

## Goals

1. **Ensure that we cannot underestimate minimum UTxO values.** 
    _It's imperative that we do not underestimate minimum UTxO quantities, as this may result in the creation of transactions that are unacceptable to the ledger. In the case of change generation, this would be particularly problematic, as change outputs are generated automatically, and users do not have direct control over the ada quantities generated._
    
2. **Decrease the complexity and fragility of minimum UTxO calculations within the wallet.**
    _The current design makes it trivial to specify a minimum UTxO function that's inappropriate for the era in which a transaction will be submitted. Using the wrong minimum UTxO function makes it possible to create transactions that will ultimately be rejected by the ledger._

## Design

We replace the existing `MinimumUTxOValue` type with the following pair of types:
```hs
data MinimumUTxO where
    MinimumUTxONone
        :: MinimumUTxO
    MinimumUTxOConstant
        :: Coin
        -> MinimumUTxO
    MinimumUTxOForShelleyBasedEraOf
        :: MinimumUTxOForShelleyBasedEra
        -> MinimumUTxO

data MinimumUTxOForShelleyBasedEra where
    MinimumUTxOForShelleyBasedEra
        :: ShelleyBasedEra era
        -> PParams (ShelleyLedgerEra era)
        -> MinimumUTxOForShelleyBasedEra
```
Where:
> - `MinimumUTxONone`
>     _Specifies that there is no minimum UTxO value._
> - `MinimumUTxOConstant`
>     _Specifies a constant minimum UTxO value._
> - `MinimumUTxOForShelleyBasedEra`
>     _Represents a minimum UTxO function for a Shelley-based era. Ensures that values can only be constructed with a set of protocol parameters that are specific to the given era._

Furthermore, we make the following change to `Primitive.Types.ProtocolParameters`:
```patch
-    , minimumUTxOvalue
-        :: MinimumUTxOValue
-        -- ^ The minimum UTxO value.
+    , minimumUTxO
+        :: MinimumUTxO
+        -- ^ Represents a way of calculating minimum UTxO values.
```

Finally, we introduce function [`computeMinimumCoinForUTxO`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/src/Cardano/Wallet/Shelley/MinimumUTxO.hs#L51):
```hs
computeMinimumCoinForUTxO :: MinimumUTxO -> TokenMap -> Coin
```
This function computes a minimum `Coin`value for a `TokenMap` that is destined for inclusion in a transaction output.

We use the above function to re-implement [`Shelley.Transaction.constraints.txOutputMinimumAdaQuantity`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/src/Cardano/Wallet/Shelley/Transaction.hs#L1500), which means that both the coin selection and migration algorithms will take advantage of this updated implementation.

## Completed QA Tasks
- [x] Transfer our existing test coverage of [`computeMinimumAdaQuantity`](https://github.com/input-output-hk/cardano-wallet/blob/4f0b2f3c3cb9af139029418f4a2ebeb166fd0691/lib/shelley/src/Cardano/Wallet/Shelley/Compatibility/Ledger.hs#L121) (including [golden tests](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/test/unit/Cardano/Wallet/Shelley/MinimumUTxOSpec.hs#L288)) to [`computeMinimumCoinForUTxO`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/src/Cardano/Wallet/Shelley/MinimumUTxO.hs#L51).
- [x] Verify that our minimum UTxO calculation is stable w.r.t. to the starting ada quantity. In particular, it should obey the following property:
     - if we call [`computeMinimumCoinForUTxO`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/src/Cardano/Wallet/Shelley/MinimumUTxO.hs#L51) and receive `Coin` value `c1`
     - if we call the Cardano API function [`calculateMinimumUTxO`](https://github.com/input-output-hk/cardano-node/blob/42809ad3d420f0695eb147d4c66b90573d27cafd/cardano-api/src/Cardano/Api/Fees.hs#L1226) with `Coin` value `c1` and receive `Coin` value `c2`
     - then `c2` **_must not_** be greater than `c1`.
   (See [`prop_computeMinimumCoinForUTxO_shelleyBasedEra_stability`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/test/unit/Cardano/Wallet/Shelley/MinimumUTxOSpec.hs#L213).)
- [x] Determine what level of test coverage is applied to the Cardano API function [`calculateMinimumUTxO`](https://github.com/input-output-hk/cardano-node/blob/42809ad3d420f0695eb147d4c66b90573d27cafd/cardano-api/src/Cardano/Api/Fees.hs#L1226). Ensure that we build appropriate test coverage to cover any deficits.
- [x] Ensure that we have test coverage to cover all valid lengths of addresses.
- [x] Ensure that we have test coverage to characterize the effect of different coin sizes (up to the limit of `txOutMaxCoin`).
- [x] Ensure that we have test coverage to characterize the effect of different token quantities (up to the limit of `txOutMaxTokenQuantity`).

## Completed Implementation Tasks

- [x] Determine the most appropriate way to initialize field `minimumUTxO` within function `fromBlockfrostPP`. See 3c03560 and ADP-1994.
- [x] Remove type `MinimumUTxOValue` from `Primitive.Types`.
- [x] Remove function [`computeMinimumAdaQuantity`](https://github.com/input-output-hk/cardano-wallet/blob/4f0b2f3c3cb9af139029418f4a2ebeb166fd0691/lib/shelley/src/Cardano/Wallet/Shelley/Compatibility/Ledger.hs#L121) and associated tests.

## Completed Admin Tasks 

- [x] Create a ticket to review and revise the behaviour of the Blockfrost protocol parameter conversion function so that it does not hard-code the choice of minimum UTxO function to the Alonzo era (which uses a coins-per-word calculation). See ADP-1994.
- [x] Create an issue on `cardano-node` to report the fact that the [`calculateMinimumUTxO`](https://github.com/input-output-hk/cardano-node/blob/42809ad3d420f0695eb147d4c66b90573d27cafd/cardano-api/src/Cardano/Api/Fees.hs#L1226) function does not appear to reach a fixed point before returning its result. (See IntersectMBO/cardano-node#4163). The current behaviour appears to be as follows:
    - If we call `calculateMinimumUTxO` with `Coin 0` and get `Coin a` back.
    - If we call `calculateMinimumUTxO` with `Coin a` and get `Coin b` back.
    - It's possible for `b` to be greater than `a`.

Co-authored-by: Jonathan Knowles <[email protected]>
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jul 13, 2022

Build failed:

Looks like it just gave up in the middle of compiling:

Building library for cardano-wallet-core-2022.7.1..
[  1 of 136] Compiling Cardano.Api.Extra ( src/Cardano/Api/Extra.hs, dist/build/Cardano/Api/Extra.o, dist/build/Cardano/Api/Extra.dyn_o )
[  2 of 136] Compiling Cardano.DB.Sqlite.Delete ( src/Cardano/DB/Sqlite/Delete.hs, dist/build/Cardano/DB/Sqlite/Delete.o, dist/build/Cardano/DB/Sqlite/Delete.dyn_o )
[  3 of 136] Compiling Cardano.Ledger.Credential.Safe ( src/Cardano/Ledger/Credential/Safe.hs, dist/build/Cardano/Ledger/Credential/Safe.o, dist/build/Cardano/Ledger/Credential/Safe.dyn_o )
[  4 of 136] Compiling Cardano.Api.Gen  ( src/Cardano/Api/Gen.hs, dist/build/Cardano/Api/Gen.o, dist/build/Cardano/Api/Gen.dyn_o )
WARNING (gitRevFromGit): Could not find git: git: readCreateProcessWithExitCode: posix_spawnp: does not exist (No such file or directory)
[  5 of 136] Compiling Cardano.Wallet.Api.Aeson.Variant ( src/Cardano/Wallet/Api/Aeson/Variant.hs, dist/build/Cardano/Wallet/Api/Aeson/Variant.o, dist/build/Cardano/Wallet/Api/Aeson/Variant.dyn_o )
[  6 of 136] Compiling Cardano.Wallet.Api.Server.Tls ( src/Cardano/Wallet/Api/Server/Tls.hs, dist/build/Cardano/Wallet/Api/Server/Tls.o, dist/build/Cardano/Wallet/Api/Server/Tls.dyn_o )
[  7 of 136] Compiling Cardano.Wallet.Checkpoints.Policy ( src/Cardano/Wallet/Checkpoints/Policy.hs, dist/build/Cardano/Wallet/Checkpoints/Policy.o, dist/build/Cardano/Wallet/Checkpoints/Policy.dyn_o )
[  8 of 136] Compiling Cardano.Wallet.CoinSelection.Internal.Context ( src/Cardano/Wallet/CoinSelection/Internal/Context.hs, dist/build/Cardano/Wallet/CoinSelection/Internal/Context.o, dist/build/Cardano/Wallet/CoinSelection/Internal/Context.dyn_o )
[  9 of 136] Compiling Cardano.Wallet.Compat ( src/Cardano/Wallet/Compat.hs, dist/build/Cardano/Wallet/Compat.o, dist/build/Cardano/Wallet/Compat.dyn_o )
[ 10 of 136] Compiling Cardano.Wallet.Logging ( src/Cardano/Wallet/Logging.hs, dist/build/Cardano/Wallet/Logging.o, dist/build/Cardano/Wallet/Logging.dyn_o )
[ 11 of 136] Compiling Cardano.Wallet.Network.Ports ( src/Cardano/Wallet/Network/Ports.hs, dist/build/Cardano/Wallet/Network/Ports.o, dist/build/Cardano/Wallet/Network/Ports.dyn_o )
[ 12 of 136] Compiling Cardano.Wallet.Orphans ( src/Cardano/Wallet/Orphans.hs, dist/build/Cardano/Wallet/Orphans.o, dist/build/Cardano/Wallet/Orphans.dyn_o )
[ 13 of 136] Compiling Cardano.Wallet.Primitive.Passphrase.Types ( src/Cardano/Wallet/Primitive/Passphrase/Types.hs, dist/build/Cardano/Wallet/Primitive/Passphrase/Types.o, dist/build/Cardano/Wallet/Primitive/Passphrase/Types.dyn_o )
[ 14 of 136] Compiling Cardano.Wallet.Primitive.Passphrase.Current ( src/Cardano/Wallet/Primitive/Passphrase/Current.hs, dist/build/Cardano/Wallet/Primitive/Passphrase/Current.o, dist/build/Cardano/Wallet/Primitive/Passphrase/Current.dyn_o )
[ 15 of 136] Compiling Cardano.Wallet.Primitive.Types.Address ( src/Cardano/Wallet/Primitive/Types/Address.hs, dist/build/Cardano/Wallet/Primitive/Types/Address.o, dist/build/Cardano/Wallet/Primitive/Types/Address.dyn_o )
[ 16 of 136] Compiling Cardano.Wallet.Address.Pool ( src/Cardano/Wallet/Address/Pool.hs, dist/build/Cardano/Wallet/Address/Pool.o, dist/build/Cardano/Wallet/Address/Pool.dyn_o )
[ 17 of 136] Compiling Cardano.Wallet.Primitive.Types.Address.Gen ( src/Cardano/Wallet/Primitive/Types/Address/Gen.hs, dist/build/Cardano/Wallet/Primitive/Types/Address/Gen.o, dist/build/Cardano/Wallet/Primitive/Types/Address/Gen.dyn_o )
[ 18 of 136] Compiling Cardano.Wallet.Primitive.Types.TokenQuantity ( src/Cardano/Wallet/Primitive/Types/TokenQuantity.hs, dist/build/Cardano/Wallet/Primitive/Types/TokenQuantity.o, dist/build/Cardano/Wallet/Primitive/Types/TokenQuantity.dyn_o )
[ 19 of 136] Compiling Cardano.Wallet.Primitive.Types.TokenQuantity.Gen ( src/Cardano/Wallet/Primitive/Types/TokenQuantity/Gen.hs, dist/build/Cardano/Wallet/Primitive/Types/TokenQuantity/Gen.o, dist/build/Cardano/Wallet/Primitive/Types/TokenQuantity/Gen.dyn_o )
[ 20 of 136] Compiling Cardano.Wallet.Util ( src/Cardano/Wallet/Util.hs, dist/build/Cardano/Wallet/Util.o, dist/build/Cardano/Wallet/Util.dyn_o )
[ 21 of 136] Compiling Cardano.Wallet.Primitive.Types.Hash ( src/Cardano/Wallet/Primitive/Types/Hash.hs, dist/build/Cardano/Wallet/Primitive/Types/Hash.o, dist/build/Cardano/Wallet/Primitive/Types/Hash.dyn_o )
[ 22 of 136] Compiling Cardano.Wallet.Primitive.Types.TokenPolicy ( src/Cardano/Wallet/Primitive/Types/TokenPolicy.hs, dist/build/Cardano/Wallet/Primitive/Types/TokenPolicy.o, dist/build/Cardano/Wallet/Primitive/Types/TokenPolicy.dyn_o )
[ 23 of 136] Compiling Cardano.Wallet.Primitive.Types.TokenPolicy.Gen ( src/Cardano/Wallet/Primitive/Types/TokenPolicy/Gen.hs, dist/build/Cardano/Wallet/Primitive/Types/TokenPolicy/Gen.o, dist/build/Cardano/Wallet/Primitive/Types/TokenPolicy/Gen.dyn_o )
[ 24 of 136] Compiling Cardano.Wallet.Primitive.Types.TokenMap ( src/Cardano/Wallet/Primitive/Types/TokenMap.hs, dist/build/Cardano/Wallet/Primitive/Types/TokenMap.o, dist/build/Cardano/Wallet/Primitive/Types/TokenMap.dyn_o )
[ 25 of 136] Compiling Cardano.Wallet.Primitive.Types.TokenMap.Gen ( src/Cardano/Wallet/Primitive/Types/TokenMap/Gen.hs, dist/build/Cardano/Wallet/Primitive/Types/TokenMap/Gen.o, dist/build/Cardano/Wallet/Primitive/Types/TokenMap/Gen.dyn_o )
[ 26 of 136] Compiling Cardano.Wallet.Primitive.Types.RewardAccount ( src/Cardano/Wallet/Primitive/Types/RewardAccount.hs, dist/build/Cardano/Wallet/Primitive/Types/RewardAccount.o, dist/build/Cardano/Wallet/Primitive/Types/RewardAccount.dyn_o )
[ 27 of 136] Compiling Cardano.Wallet.Primitive.Types.RewardAccount.Gen ( src/Cardano/Wallet/Primitive/Types/RewardAccount/Gen.hs, dist/build/Cardano/Wallet/Primitive/Types/RewardAccount/Gen.o, dist/build/Cardano/Wallet/Primitive/Types/RewardAccount/Gen.dyn_o )
[ 28 of 136] Compiling Cardano.Wallet.Version.TH ( src/Cardano/Wallet/Version/TH.hs, dist/build/Cardano/Wallet/Version/TH.o, dist/build/Cardano/Wallet/Version/TH.dyn_o )
[ 29 of 136] Compiling Control.Concurrent.Concierge ( src/Control/Concurrent/Concierge.hs, dist/build/Control/Concurrent/Concierge.o, dist/build/Control/Concurrent/Concierge.dyn_o )
[ 30 of 136] Compiling Control.Monad.Exception.Unchecked ( src/Control/Monad/Exception/Unchecked.hs, dist/build/Control/Monad/Exception/Unchecked.o, dist/build/Control/Monad/Exception/Unchecked.dyn_o )
[ 31 of 136] Compiling Crypto.Hash.Utils ( src/Crypto/Hash/Utils.hs, dist/build/Crypto/Hash/Utils.o, dist/build/Crypto/Hash/Utils.dyn_o )
[ 32 of 136] Compiling Cardano.Wallet.Primitive.Passphrase.Legacy ( src/Cardano/Wallet/Primitive/Passphrase/Legacy.hs, dist/build/Cardano/Wallet/Primitive/Passphrase/Legacy.o, dist/build/Cardano/Wallet/Primitive/Passphrase/Legacy.dyn_o )
[ 33 of 136] Compiling Cardano.Wallet.Primitive.Passphrase ( src/Cardano/Wallet/Primitive/Passphrase.hs, dist/build/Cardano/Wallet/Primitive/Passphrase.o, dist/build/Cardano/Wallet/Primitive/Passphrase.dyn_o )
[ 34 of 136] Compiling Cardano.Wallet.Primitive.Passphrase.Gen ( src/Cardano/Wallet/Primitive/Passphrase/Gen.hs, dist/build/Cardano/Wallet/Primitive/Passphrase/Gen.o, dist/build/Cardano/Wallet/Primitive/Passphrase/Gen.dyn_o )
[ 35 of 136] Compiling Cardano.Wallet.Primitive.AddressDerivation ( src/Cardano/Wallet/Primitive/AddressDerivation.hs, dist/build/Cardano/Wallet/Primitive/AddressDerivation.o, dist/build/Cardano/Wallet/Primitive/AddressDerivation.dyn_o )
[ 36 of 136] Compiling Cardano.Wallet.Primitive.AddressDerivation.SharedKey ( src/Cardano/Wallet/Primitive/AddressDerivation/SharedKey.hs, dist/build/Cardano/Wallet/Primitive/AddressDerivation/SharedKey.o, dist/build/Cardano/Wallet/Primitive/AddressDerivation/SharedKey.dyn_o )
[ 37 of 136] Compiling Data.Aeson.Extra ( src/Data/Aeson/Extra.hs, dist/build/Data/Aeson/Extra.o, dist/build/Data/Aeson/Extra.dyn_o )
[ 38 of 136] Compiling Control.Monad.Random.Extra ( src/Control/Monad/Random/Extra.hs, dist/build/Control/Monad/Random/Extra.o, dist/build/Control/Monad/Random/Extra.dyn_o )
[ 39 of 136] Compiling Data.Function.Utils ( src/Data/Function/Utils.hs, dist/build/Data/Function/Utils.o, dist/build/Data/Function/Utils.dyn_o )
[ 40 of 136] Compiling Data.Quantity    ( src/Data/Quantity.hs, dist/build/Data/Quantity.o, dist/build/Data/Quantity.dyn_o )
[ 41 of 136] Compiling Cardano.Wallet.Primitive.Types.Coin ( src/Cardano/Wallet/Primitive/Types/Coin.hs, dist/build/Cardano/Wallet/Primitive/Types/Coin.o, dist/build/Cardano/Wallet/Primitive/Types/Coin.dyn_o )
[ 42 of 136] Compiling Cardano.Wallet.Primitive.Types.TokenBundle ( src/Cardano/Wallet/Primitive/Types/TokenBundle.hs, dist/build/Cardano/Wallet/Primitive/Types/TokenBundle.o, dist/build/Cardano/Wallet/Primitive/Types/TokenBundle.dyn_o )
[ 43 of 136] Compiling Cardano.Wallet.Primitive.Types.UTxOIndex.Internal ( src/Cardano/Wallet/Primitive/Types/UTxOIndex/Internal.hs, dist/build/Cardano/Wallet/Primitive/Types/UTxOIndex/Internal.o, dist/build/Cardano/Wallet/Primitive/Types/UTxOIndex/Internal.dyn_o )
(no further output)

@jonathanknowles
Copy link
Member Author

I've restarted all failed jobs on hydra and verified that they all pass:
https://hydra.iohk.io/build/16764644

I'll make another merge attempt.

@jonathanknowles
Copy link
Member Author

bors r+

iohk-bors bot added a commit that referenced this pull request Jul 13, 2022
3368: Use Cardano API function `calculateMinimumUTxO` for minimum UTxO calculations. r=jonathanknowles a=jonathanknowles

## Issue Number 

ADP-1978

## Summary

This PR uses the Cardano API function [`calculateMinimumUTxO`](https://github.com/input-output-hk/cardano-node/blob/42809ad3d420f0695eb147d4c66b90573d27cafd/cardano-api/src/Cardano/Api/Fees.hs#L1226) to replace our hand-coded [`computeMinimumAdaQuantity`](https://github.com/input-output-hk/cardano-wallet/blob/4f0b2f3c3cb9af139029418f4a2ebeb166fd0691/lib/shelley/src/Cardano/Wallet/Shelley/Compatibility/Ledger.hs#L121) function.

## Goals

1. **Ensure that we cannot underestimate minimum UTxO values.** 
    _It's imperative that we do not underestimate minimum UTxO quantities, as this may result in the creation of transactions that are unacceptable to the ledger. In the case of change generation, this would be particularly problematic, as change outputs are generated automatically, and users do not have direct control over the ada quantities generated._
    
2. **Decrease the complexity and fragility of minimum UTxO calculations within the wallet.**
    _The current design makes it trivial to specify a minimum UTxO function that's inappropriate for the era in which a transaction will be submitted. Using the wrong minimum UTxO function makes it possible to create transactions that will ultimately be rejected by the ledger._

## Design

We replace the existing `MinimumUTxOValue` type with the following pair of types:
```hs
data MinimumUTxO where
    MinimumUTxONone
        :: MinimumUTxO
    MinimumUTxOConstant
        :: Coin
        -> MinimumUTxO
    MinimumUTxOForShelleyBasedEraOf
        :: MinimumUTxOForShelleyBasedEra
        -> MinimumUTxO

data MinimumUTxOForShelleyBasedEra where
    MinimumUTxOForShelleyBasedEra
        :: ShelleyBasedEra era
        -> PParams (ShelleyLedgerEra era)
        -> MinimumUTxOForShelleyBasedEra
```
Where:
> - `MinimumUTxONone`
>     _Specifies that there is no minimum UTxO value._
> - `MinimumUTxOConstant`
>     _Specifies a constant minimum UTxO value._
> - `MinimumUTxOForShelleyBasedEra`
>     _Represents a minimum UTxO function for a Shelley-based era. Ensures that values can only be constructed with a set of protocol parameters that are specific to the given era._

Furthermore, we make the following change to `Primitive.Types.ProtocolParameters`:
```patch
-    , minimumUTxOvalue
-        :: MinimumUTxOValue
-        -- ^ The minimum UTxO value.
+    , minimumUTxO
+        :: MinimumUTxO
+        -- ^ Represents a way of calculating minimum UTxO values.
```

Finally, we introduce function [`computeMinimumCoinForUTxO`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/src/Cardano/Wallet/Shelley/MinimumUTxO.hs#L51):
```hs
computeMinimumCoinForUTxO :: MinimumUTxO -> TokenMap -> Coin
```
This function computes a minimum `Coin`value for a `TokenMap` that is destined for inclusion in a transaction output.

We use the above function to re-implement [`Shelley.Transaction.constraints.txOutputMinimumAdaQuantity`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/src/Cardano/Wallet/Shelley/Transaction.hs#L1500), which means that both the coin selection and migration algorithms will take advantage of this updated implementation.

## Completed QA Tasks
- [x] Transfer our existing test coverage of [`computeMinimumAdaQuantity`](https://github.com/input-output-hk/cardano-wallet/blob/4f0b2f3c3cb9af139029418f4a2ebeb166fd0691/lib/shelley/src/Cardano/Wallet/Shelley/Compatibility/Ledger.hs#L121) (including [golden tests](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/test/unit/Cardano/Wallet/Shelley/MinimumUTxOSpec.hs#L288)) to [`computeMinimumCoinForUTxO`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/src/Cardano/Wallet/Shelley/MinimumUTxO.hs#L51).
- [x] Verify that our minimum UTxO calculation is stable w.r.t. to the starting ada quantity. In particular, it should obey the following property:
     - if we call [`computeMinimumCoinForUTxO`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/src/Cardano/Wallet/Shelley/MinimumUTxO.hs#L51) and receive `Coin` value `c1`
     - if we call the Cardano API function [`calculateMinimumUTxO`](https://github.com/input-output-hk/cardano-node/blob/42809ad3d420f0695eb147d4c66b90573d27cafd/cardano-api/src/Cardano/Api/Fees.hs#L1226) with `Coin` value `c1` and receive `Coin` value `c2`
     - then `c2` **_must not_** be greater than `c1`.
   (See [`prop_computeMinimumCoinForUTxO_shelleyBasedEra_stability`](https://github.com/input-output-hk/cardano-wallet/blob/a2c414ad3e44d1af900280b9a890bb398783e400/lib/shelley/test/unit/Cardano/Wallet/Shelley/MinimumUTxOSpec.hs#L213).)
- [x] Determine what level of test coverage is applied to the Cardano API function [`calculateMinimumUTxO`](https://github.com/input-output-hk/cardano-node/blob/42809ad3d420f0695eb147d4c66b90573d27cafd/cardano-api/src/Cardano/Api/Fees.hs#L1226). Ensure that we build appropriate test coverage to cover any deficits.
- [x] Ensure that we have test coverage to cover all valid lengths of addresses.
- [x] Ensure that we have test coverage to characterize the effect of different coin sizes (up to the limit of `txOutMaxCoin`).
- [x] Ensure that we have test coverage to characterize the effect of different token quantities (up to the limit of `txOutMaxTokenQuantity`).

## Completed Implementation Tasks

- [x] Determine the most appropriate way to initialize field `minimumUTxO` within function `fromBlockfrostPP`. See 3c03560 and ADP-1994.
- [x] Remove type `MinimumUTxOValue` from `Primitive.Types`.
- [x] Remove function [`computeMinimumAdaQuantity`](https://github.com/input-output-hk/cardano-wallet/blob/4f0b2f3c3cb9af139029418f4a2ebeb166fd0691/lib/shelley/src/Cardano/Wallet/Shelley/Compatibility/Ledger.hs#L121) and associated tests.

## Completed Admin Tasks 

- [x] Create a ticket to review and revise the behaviour of the Blockfrost protocol parameter conversion function so that it does not hard-code the choice of minimum UTxO function to the Alonzo era (which uses a coins-per-word calculation). See ADP-1994.
- [x] Create an issue on `cardano-node` to report the fact that the [`calculateMinimumUTxO`](https://github.com/input-output-hk/cardano-node/blob/42809ad3d420f0695eb147d4c66b90573d27cafd/cardano-api/src/Cardano/Api/Fees.hs#L1226) function does not appear to reach a fixed point before returning its result. (See IntersectMBO/cardano-node#4163). The current behaviour appears to be as follows:
    - If we call `calculateMinimumUTxO` with `Coin 0` and get `Coin a` back.
    - If we call `calculateMinimumUTxO` with `Coin a` and get `Coin b` back.
    - It's possible for `b` to be greater than `a`.

Co-authored-by: Jonathan Knowles <[email protected]>
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jul 13, 2022

Build failed:

@Anviking
Copy link
Member

bors r+

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jul 13, 2022

Build succeeded:

@iohk-bors iohk-bors bot merged commit dce6103 into master Jul 13, 2022
@iohk-bors iohk-bors bot deleted the jonathanknowles/use-cardano-api-calculateMinimumUTxO branch July 13, 2022 10:07
WilliamKingNoel-Bot pushed a commit that referenced this pull request Jul 13, 2022
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.

2 participants