Skip to content

Commit

Permalink
Remove the restriction that all minted assets must be spent or burned.
Browse files Browse the repository at this point in the history
This commit only makes the minimum amount of changes necessary to ensure
that the code still compiles.

Further work will be required to verify correctness and completeness.
  • Loading branch information
jonathanknowles committed Sep 12, 2021
1 parent cfb94ac commit 5e63779
Show file tree
Hide file tree
Showing 3 changed files with 0 additions and 93 deletions.
8 changes: 0 additions & 8 deletions lib/core/src/Cardano/Wallet/Api/Server.hs
Expand Up @@ -361,7 +361,6 @@ import Cardano.Wallet.Primitive.CoinSelection.Balance
( SelectionResult (..)
, UnableToConstructChangeError (..)
, balanceMissing
, missingOutputAssets
, selectionDelta
)
import Cardano.Wallet.Primitive.Delegation.UTxO
Expand Down Expand Up @@ -3821,13 +3820,6 @@ instance IsServerError ErrSelectAssets where
, "must specify enough ada. Here are the problematic "
, "outputs:\n" <> pretty (indentF 2 $ blockListF xs)
]
Balance.OutputsInsufficient e ->
apiError err403 TokensMintedButNotSpentOrBurned $ mconcat
[ "I can't process this transaction because some "
, "minted values were not spent or burned. These "
, "are the values that should be spent or burned: "
, pretty . Flat $ missingOutputAssets e
]
Balance.UnableToConstructChange e ->
apiError err403 CannotCoverFee $ T.unwords
[ "I am unable to finalize the transaction, as there"
Expand Down
53 changes: 0 additions & 53 deletions lib/core/src/Cardano/Wallet/Primitive/CoinSelection/Balance.hs
Expand Up @@ -39,7 +39,6 @@ module Cardano.Wallet.Primitive.CoinSelection.Balance
, SelectionResult (..)
, SelectionError (..)
, BalanceInsufficientError (..)
, OutputsInsufficientError (..)
, SelectionInsufficientError (..)
, InsufficientMinCoinValueError (..)
, UnableToConstructChangeError (..)
Expand Down Expand Up @@ -103,7 +102,6 @@ module Cardano.Wallet.Primitive.CoinSelection.Balance
, distance
, mapMaybe
, balanceMissing
, missingOutputAssets
) where

import Prelude
Expand Down Expand Up @@ -312,55 +310,10 @@ data SelectionError
SelectionInsufficientError
| InsufficientMinCoinValues
(NonEmpty InsufficientMinCoinValueError)
| OutputsInsufficient
OutputsInsufficientError
| UnableToConstructChange
UnableToConstructChangeError
deriving (Generic, Eq, Show)

-- | Indicates that a portion of the minted assets were not spent or burned.
--
-- This situation occurs if the following inequality does not hold:
--
-- >>> assetsToMint `leq` (assetsToBurn <> requestedOutputAssets)
--
-- The existence of this error reflects a deliberate design choice: all minted
-- assets must either be explicitly spent or explicitly burned by the caller.
-- In future, we could revise this design to allow excess minted assets (those
-- that are neither spent nor burned) to be returned to the wallet as change.
--
data OutputsInsufficientError = OutputsInsufficientError
{ assetsToMint
:: !TokenMap
-- ^ The assets to mint
, assetsToBurn
:: !TokenMap
-- ^ The assets to burn
, requestedOutputAssets
:: !TokenMap
-- ^ The complete set of assets found within the user-specified outputs
} deriving (Generic, Eq, Show)

-- | Computes the portion of minted assets that are not spent or burned.
--
missingOutputAssets :: OutputsInsufficientError -> TokenMap
missingOutputAssets e =
-- We use 'difference' which will show us the quantities in 'assetsToMint'
-- that are not in 'assetsToBurn <> requestedOutputAssets'.
--
-- Any asset quantity present in 'assetsToBurn <> requestedOutputAssets'
-- but not present in 'assetsToMint' will simply be zeroed out, which is
-- the behaviour we want for this error report.
--
assetsToMint `TokenMap.difference`
(assetsToBurn `TokenMap.add` requestedOutputAssets)
where
OutputsInsufficientError
{ assetsToMint
, assetsToBurn
, requestedOutputAssets
} = e

-- | Indicates that the balance of inputs actually selected was insufficient to
-- cover the balance of 'outputsToCover'.
--
Expand Down Expand Up @@ -497,11 +450,6 @@ performSelection
-- ^ The selection goal to satisfy.
-> m (Either SelectionError (SelectionResult TokenBundle))
performSelection minCoinFor costFor bundleSizeAssessor criteria
-- Is the minted value all spent or burnt?
| not (assetsToMint `leq` (assetsToBurn <> requestedOutputAssets)) =
pure $ Left $ OutputsInsufficient $ OutputsInsufficientError
{assetsToMint, assetsToBurn, requestedOutputAssets}

-- Is the total available balance sufficient?
| not (balanceRequired `leq` balanceAvailable) =
pure $ Left $ BalanceInsufficient $ BalanceInsufficientError
Expand Down Expand Up @@ -546,7 +494,6 @@ performSelection minCoinFor costFor bundleSizeAssessor criteria
}

requestedOutputs = F.foldMap (view #tokens) outputsToCover
requestedOutputAssets = view #tokens requestedOutputs

mkInputsSelected :: UTxOIndex -> NonEmpty (TxIn, TxOut)
mkInputsSelected =
Expand Down
Expand Up @@ -27,7 +27,6 @@ import Cardano.Wallet.Primitive.CoinSelection.Balance
, BalanceInsufficientError (..)
, InsufficientMinCoinValueError (..)
, MakeChangeCriteria (..)
, OutputsInsufficientError (..)
, RunSelectionParams (..)
, SelectionCriteria (..)
, SelectionError (..)
Expand All @@ -54,7 +53,6 @@ import Cardano.Wallet.Primitive.CoinSelection.Balance
, makeChangeForNonUserSpecifiedAssets
, makeChangeForUserSpecifiedAsset
, mapMaybe
, missingOutputAssets
, performSelection
, prepareOutputsWith
, reduceTokenQuantities
Expand Down Expand Up @@ -745,8 +743,6 @@ prop_performSelection_small minCoinValueFor costFor (Blind (Small criteria)) =
"selection limited but sufficient"
. cover 10 (selectionLimited && selectionInsufficient result)
"selection limited and insufficient"
. cover 2 (outputsInsufficient result)
"A portion of the minted assets were not spent or burned"
where
utxoHasAtLeastOneAsset = not
. Set.null
Expand All @@ -761,11 +757,6 @@ prop_performSelection_small minCoinValueFor costFor (Blind (Small criteria)) =
. fmap (view #tokens)
$ outputsToCover criteria

outputsInsufficient :: PerformSelectionResult -> Bool
outputsInsufficient = \case
Left (OutputsInsufficient _) -> True
_ -> False

selectionLimited :: Bool
selectionLimited = case view #selectionLimit criteria of
MaximumInputLimit _ -> True
Expand Down Expand Up @@ -962,8 +953,6 @@ prop_performSelection minCoinValueFor costFor (Blind criteria) coverage =
onFailure = \case
BalanceInsufficient e ->
onBalanceInsufficient e
OutputsInsufficient e ->
onOutputsInsufficient e
SelectionInsufficient e ->
onSelectionInsufficient e
InsufficientMinCoinValues es ->
Expand Down Expand Up @@ -1025,27 +1014,6 @@ prop_performSelection minCoinValueFor costFor (Blind criteria) coverage =
errorBalanceSelected =
F.foldMap (view #tokens . snd) errorInputsSelected

onOutputsInsufficient e = do
monitor $ counterexample $ unlines
[ "assets to mint:"
, pretty (Flat errorAssetsToMint)
, "assets to burn:"
, pretty (Flat errorAssetsToBurn)
, "requested output assets:"
, pretty (Flat errorRequestedOutputAssets)
, "assets minted but not spent or burned:"
, pretty (Flat $ missingOutputAssets e)
]
assert $ errorAssetsToMint == assetsToMint
assert $ errorAssetsToBurn == assetsToBurn
assert
$ not
$ errorAssetsToMint
`leq` (errorRequestedOutputAssets <> errorAssetsToBurn)
where
OutputsInsufficientError
errorAssetsToMint errorAssetsToBurn errorRequestedOutputAssets = e

onInsufficientMinCoinValues es = do
monitor $ counterexample $ unlines
[ show es
Expand Down

0 comments on commit 5e63779

Please sign in to comment.