Skip to content

Commit

Permalink
Merge #3109
Browse files Browse the repository at this point in the history
3109: Provide a wallet-specific interface for coin selection. r=jonathanknowles a=jonathanknowles

## Issue Number

ADP-1407

## Summary

This PR reorganizes the coin selection module hierarchy to have the following structure:

- `Cardano.Wallet.CoinSelection`
 provides a stable, **wallet-specific** interface for coin selection, with wallet-friendly types such as `TxIn`, `TxOut`, and `UTxO`.
- `Cardano.Wallet.CoinSelection.Internal.*`
 provides **internal** functions and types, whose definitions are allowed to deviate from those provided in the public module. 
 
This PR also:
- adjusts all parts of the wallet that need coin selection functionality to import from `Cardano.Wallet.CoinSelection`, rather than `Cardano.Wallet.CoinSelection.Internal`.
- adjusts the names of some error types and constructors to avoid name collisions.

Co-authored-by: Jonathan Knowles <[email protected]>
  • Loading branch information
iohk-bors[bot] and jonathanknowles authored Feb 7, 2022
2 parents 84d1aa7 + 72dd574 commit 8bc24b7
Show file tree
Hide file tree
Showing 17 changed files with 267 additions and 173 deletions.
15 changes: 8 additions & 7 deletions lib/core/cardano-wallet-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ library
Cardano.Wallet.Api.Server
Cardano.Wallet.Api.Server.Tls
Cardano.Wallet.Api.Types
Cardano.Wallet.CoinSelection
Cardano.Wallet.CoinSelection.Internal
Cardano.Wallet.CoinSelection.Internal.Balance
Cardano.Wallet.CoinSelection.Internal.Collateral
Cardano.Wallet.Compat
Cardano.Wallet.DB
Cardano.Wallet.DB.Checkpoints
Expand Down Expand Up @@ -206,9 +210,6 @@ library
Cardano.Wallet.Primitive.AddressDiscovery.Sequential
Cardano.Wallet.Primitive.AddressDiscovery.Shared
Cardano.Wallet.Primitive.SyncProgress
Cardano.Wallet.Primitive.CoinSelection
Cardano.Wallet.Primitive.CoinSelection.Balance
Cardano.Wallet.Primitive.CoinSelection.Collateral
Cardano.Wallet.Primitive.Collateral
Cardano.Wallet.Primitive.Delegation.UTxO
Cardano.Wallet.Primitive.Migration
Expand Down Expand Up @@ -259,7 +260,7 @@ library
-- The following modules define QC generators and shrinkers that can
-- be used by both `cardano-wallet-core` and `cardano-wallet`:
--
Cardano.Wallet.Primitive.CoinSelection.Balance.Gen
Cardano.Wallet.CoinSelection.Internal.Balance.Gen
Cardano.Wallet.Primitive.Types.Address.Gen
Cardano.Wallet.Primitive.Types.Coin.Gen
Cardano.Wallet.Primitive.Types.RewardAccount.Gen
Expand Down Expand Up @@ -414,6 +415,9 @@ test-suite unit
Cardano.Wallet.Api.ServerSpec
Cardano.Wallet.Api.TypesSpec
Cardano.Wallet.ApiSpec
Cardano.Wallet.CoinSelection.InternalSpec
Cardano.Wallet.CoinSelection.Internal.BalanceSpec
Cardano.Wallet.CoinSelection.Internal.CollateralSpec
Cardano.Wallet.DB.Arbitrary
Cardano.Wallet.DB.MVarSpec
Cardano.Wallet.DB.Properties
Expand All @@ -432,9 +436,6 @@ test-suite unit
Cardano.Wallet.Primitive.AddressDiscovery.SharedSpec
Cardano.Wallet.Primitive.Delegation.StateSpec
Cardano.Wallet.Primitive.AddressDiscoverySpec
Cardano.Wallet.Primitive.CoinSelectionSpec
Cardano.Wallet.Primitive.CoinSelection.BalanceSpec
Cardano.Wallet.Primitive.CoinSelection.CollateralSpec
Cardano.Wallet.Primitive.CollateralSpec
Cardano.Wallet.Primitive.MigrationSpec
Cardano.Wallet.Primitive.Migration.PlanningSpec
Expand Down
43 changes: 22 additions & 21 deletions lib/core/src/Cardano/Wallet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,25 @@ import Cardano.Crypto.Wallet
( toXPub )
import Cardano.Slotting.Slot
( SlotNo (..) )
import Cardano.Wallet.CoinSelection
( Selection
, SelectionBalanceError (..)
, SelectionCollateralRequirement (..)
, SelectionConstraints (..)
, SelectionError (..)
, SelectionOf (..)
, SelectionOutputError (..)
, SelectionParams (..)
, SelectionReportDetailed
, SelectionReportSummarized
, SelectionSkeleton (..)
, UnableToConstructChangeError (..)
, emptySkeleton
, makeSelectionReportDetailed
, makeSelectionReportSummarized
, performSelection
, selectionDelta
)
import Cardano.Wallet.DB
( DBLayer (..)
, ErrNoSuchTransaction (..)
Expand Down Expand Up @@ -296,23 +315,6 @@ import Cardano.Wallet.Primitive.AddressDiscovery.Shared
, SharedState (..)
, addCosignerAccXPub
)
import Cardano.Wallet.Primitive.CoinSelection
( Selection
, SelectionCollateralRequirement (..)
, SelectionConstraints (..)
, SelectionError (..)
, SelectionOf (..)
, SelectionOutputInvalidError (..)
, SelectionParams (..)
, SelectionReportDetailed
, SelectionReportSummarized
, makeSelectionReportDetailed
, makeSelectionReportSummarized
, performSelection
, selectionDelta
)
import Cardano.Wallet.Primitive.CoinSelection.Balance
( SelectionSkeleton (..), emptySkeleton )
import Cardano.Wallet.Primitive.Collateral
( asCollateral )
import Cardano.Wallet.Primitive.Migration
Expand Down Expand Up @@ -550,7 +552,6 @@ import qualified Cardano.Crypto.Wallet as CC
import qualified Cardano.Wallet.Primitive.AddressDiscovery.Random as Rnd
import qualified Cardano.Wallet.Primitive.AddressDiscovery.Sequential as Seq
import qualified Cardano.Wallet.Primitive.AddressDiscovery.Shared as Shared
import qualified Cardano.Wallet.Primitive.CoinSelection.Balance as Balance
import qualified Cardano.Wallet.Primitive.Migration as Migration
import qualified Cardano.Wallet.Primitive.Types as W
import qualified Cardano.Wallet.Primitive.Types.Coin as Coin
Expand Down Expand Up @@ -2690,9 +2691,9 @@ estimateFee
handleCannotCover :: ErrSelectAssets -> ExceptT ErrSelectAssets m Coin
handleCannotCover = \case
e@(ErrSelectAssetsSelectionError se) -> case se of
SelectionBalanceError (Balance.UnableToConstructChange ce) ->
SelectionBalanceErrorOf (UnableToConstructChange ce) ->
case ce of
Balance.UnableToConstructChangeError {requiredCost} ->
UnableToConstructChangeError {requiredCost} ->
pure requiredCost
_ ->
throwE e
Expand Down Expand Up @@ -3161,7 +3162,7 @@ data ErrCreateMigrationPlan
deriving (Generic, Eq, Show)

data ErrSelectAssets
= ErrSelectAssetsPrepareOutputsError SelectionOutputInvalidError
= ErrSelectAssetsPrepareOutputsError SelectionOutputError
| ErrSelectAssetsNoSuchWallet ErrNoSuchWallet
| ErrSelectAssetsAlreadyWithdrawing Tx
| ErrSelectAssetsSelectionError SelectionError
Expand Down
46 changes: 23 additions & 23 deletions lib/core/src/Cardano/Wallet/Api/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@ import Cardano.Wallet.Api.Types
, toApiNetworkParameters
, toApiUtxoStatistics
)
import Cardano.Wallet.CoinSelection
( SelectionBalanceError (..)
, SelectionCollateralError
, SelectionError (..)
, SelectionOf (..)
, SelectionOutputError (..)
, SelectionOutputSizeExceedsLimitError (..)
, SelectionOutputTokenQuantityExceedsLimitError (..)
, balanceMissing
, selectionDelta
, shortfall
)
import Cardano.Wallet.Compat
( (^?) )
import Cardano.Wallet.DB
Expand Down Expand Up @@ -370,16 +382,6 @@ import Cardano.Wallet.Primitive.AddressDiscovery.Shared
, mkSharedStateFromRootXPrv
, validateScriptTemplates
)
import Cardano.Wallet.Primitive.CoinSelection
( SelectionError (..)
, SelectionOf (..)
, SelectionOutputInvalidError (..)
, SelectionOutputSizeExceedsLimitError (..)
, SelectionOutputTokenQuantityExceedsLimitError (..)
, selectionDelta
)
import Cardano.Wallet.Primitive.CoinSelection.Balance
( UnableToConstructChangeError (..), balanceMissing )
import Cardano.Wallet.Primitive.Delegation.UTxO
( stakeKeyCoinDistr )
import Cardano.Wallet.Primitive.Migration
Expand Down Expand Up @@ -607,8 +609,6 @@ import qualified Cardano.Wallet.Network as NW
import qualified Cardano.Wallet.Primitive.AddressDerivation.Byron as Byron
import qualified Cardano.Wallet.Primitive.AddressDerivation.Icarus as Icarus
import qualified Cardano.Wallet.Primitive.AddressDiscovery.Shared as Shared
import qualified Cardano.Wallet.Primitive.CoinSelection.Balance as Balance
import qualified Cardano.Wallet.Primitive.CoinSelection.Collateral as Collateral
import qualified Cardano.Wallet.Primitive.Types as W
import qualified Cardano.Wallet.Primitive.Types.Coin as Coin
import qualified Cardano.Wallet.Primitive.Types.TokenBundle as TokenBundle
Expand Down Expand Up @@ -4252,7 +4252,7 @@ instance IsServerError (ErrInvalidDerivationIndex 'Soft level) where
, "between ", pretty minIx, " and ", pretty maxIx, " without a suffix."
]

instance IsServerError SelectionOutputInvalidError where
instance IsServerError SelectionOutputError where
toServerError = \case
SelectionOutputSizeExceedsLimit e ->
toServerError e
Expand Down Expand Up @@ -4317,30 +4317,30 @@ instance IsServerError ErrSelectAssets where
, "transaction; if, for some reason, you really want a new "
, "transaction, then cancel the previous one first."
]
ErrSelectAssetsSelectionError (SelectionBalanceError e) ->
ErrSelectAssetsSelectionError (SelectionBalanceErrorOf e) ->
toServerError e
ErrSelectAssetsSelectionError (SelectionCollateralError e) ->
ErrSelectAssetsSelectionError (SelectionCollateralErrorOf e) ->
toServerError e
ErrSelectAssetsSelectionError (SelectionOutputError e) ->
ErrSelectAssetsSelectionError (SelectionOutputErrorOf e) ->
toServerError e

instance IsServerError (Balance.SelectionError) where
instance IsServerError (SelectionBalanceError) where
toServerError = \case
Balance.BalanceInsufficient e ->
BalanceInsufficient e ->
apiError err403 NotEnoughMoney $ mconcat
[ "I can't process this payment as there are not "
, "enough funds available in the wallet. I am "
, "missing: ", pretty . Flat $ balanceMissing e
]
Balance.SelectionLimitReached e ->
SelectionLimitReached e ->
apiError err403 TransactionIsTooBig $ mconcat
[ "I am not able to finalize the transaction "
, "because I need to select additional inputs and "
, "doing so will make the transaction too big. Try "
, "sending a smaller amount. I had already selected "
, showT (length $ view #inputsSelected e), " inputs."
]
Balance.InsufficientMinCoinValues xs ->
InsufficientMinCoinValues xs ->
apiError err403 UtxoTooSmall $ mconcat
[ "Some outputs have ada values that are too small. "
, "There's a minimum ada value specified by the "
Expand All @@ -4350,7 +4350,7 @@ instance IsServerError (Balance.SelectionError) where
, "must specify enough ada. Here are the problematic "
, "outputs:\n" <> pretty (indentF 2 $ blockListF xs)
]
Balance.UnableToConstructChange e ->
UnableToConstructChange e ->
apiError err403 CannotCoverFee $ T.unwords
[ "I am unable to finalize the transaction, as there"
, "is not enough ada available to pay for the fee and"
Expand All @@ -4360,14 +4360,14 @@ instance IsServerError (Balance.SelectionError) where
, "ada to proceed. Try increasing your wallet balance"
, "or sending a smaller amount."
]
Balance.EmptyUTxO ->
EmptyUTxO ->
apiError err403 NotEnoughMoney $ T.unwords
[ "Cannot create a transaction because the wallet"
, "has no UTxO entries. At least one UTxO entry is"
, "required in order to create a transaction."
]

instance IsServerError (Collateral.SelectionError) where
instance IsServerError SelectionCollateralError where
toServerError e =
apiError err403 InsufficientCollateral $ T.unwords
[ "I'm unable to create this transaction because the balance"
Expand Down
82 changes: 82 additions & 0 deletions lib/core/src/Cardano/Wallet/CoinSelection.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
-- |
-- Copyright: © 2022 IOHK
-- License: Apache-2.0
--
-- This module provides a wallet-specific interface for coin selection.
--
-- Coin selection handles the following responsibilities:
--
-- - selecting inputs from the UTxO set to pay for user-specified outputs;
-- - selecting inputs from the UTxO set to pay for collateral;
-- - producing change outputs to return excess value to the wallet;
-- - balancing a selection to pay for the transaction fee.
--
-- Use the 'performSelection' function to perform a coin selection.
--
module Cardano.Wallet.CoinSelection
(
-- * Performing selections
performSelection
, Selection
, SelectionCollateralRequirement (..)
, SelectionConstraints (..)
, SelectionError (..)
, SelectionLimit
, SelectionLimitOf (..)
, SelectionOf (..)
, SelectionParams (..)

-- * Selection skeletons
, SelectionSkeleton (..)
, emptySkeleton

-- * Selection errors
, BalanceInsufficientError (..)
, SelectionBalanceError (..)
, SelectionCollateralError
, SelectionOutputError (..)
, SelectionOutputSizeExceedsLimitError (..)
, SelectionOutputTokenQuantityExceedsLimitError (..)
, UnableToConstructChangeError (..)

-- * Selection reports
, makeSelectionReportDetailed
, makeSelectionReportSummarized
, SelectionReportDetailed
, SelectionReportSummarized

-- * Selection deltas
, balanceMissing
, selectionDelta
)
where

import Cardano.Wallet.CoinSelection.Internal
( Selection
, SelectionCollateralRequirement (..)
, SelectionConstraints (..)
, SelectionError (..)
, SelectionOf (..)
, SelectionOutputError (..)
, SelectionOutputSizeExceedsLimitError (..)
, SelectionOutputTokenQuantityExceedsLimitError (..)
, SelectionParams (..)
, SelectionReportDetailed
, SelectionReportSummarized
, makeSelectionReportDetailed
, makeSelectionReportSummarized
, performSelection
, selectionDelta
)
import Cardano.Wallet.CoinSelection.Internal.Balance
( BalanceInsufficientError (..)
, SelectionBalanceError (..)
, SelectionLimit
, SelectionLimitOf (..)
, SelectionSkeleton (..)
, UnableToConstructChangeError (..)
, balanceMissing
, emptySkeleton
)
import Cardano.Wallet.CoinSelection.Internal.Collateral
( SelectionCollateralError )
Loading

0 comments on commit 8bc24b7

Please sign in to comment.