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

Provide a wallet-specific interface for coin selection. #3109

Merged
merged 2 commits into from
Feb 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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