Skip to content

Commit 6854799

Browse files
committed
Handle non-spendable utxos when selecting collateral
1 parent be434c4 commit 6854799

File tree

1 file changed

+54
-26
lines changed

1 file changed

+54
-26
lines changed

src/Internal/BalanceTx/BalanceTx.purs

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Cardano.Types
2222
, Transaction
2323
, TransactionBody
2424
, TransactionOutput
25+
, TransactionUnspentOutput
2526
, UtxoMap
2627
, Value(Value)
2728
, _amount
@@ -41,9 +42,10 @@ import Cardano.Types.Address (Address)
4142
import Cardano.Types.BigNum as BigNum
4243
import Cardano.Types.Coin as Coin
4344
import Cardano.Types.OutputDatum (OutputDatum(OutputDatum))
44-
import Cardano.Types.TransactionBody (_votingProposals)
45+
import Cardano.Types.TransactionBody (_collateral, _votingProposals)
4546
import Cardano.Types.TransactionInput (TransactionInput)
46-
import Cardano.Types.TransactionUnspentOutput as TransactionUnspentOutputs
47+
import Cardano.Types.TransactionUnspentOutput (_output)
48+
import Cardano.Types.TransactionUnspentOutput as TransactionUnspentOutput
4749
import Cardano.Types.TransactionWitnessSet (_redeemers)
4850
import Cardano.Types.UtxoMap (pprintUtxoMap)
4951
import Cardano.Types.Value (getMultiAsset, mkValue, pprintValue)
@@ -65,7 +67,10 @@ import Ctl.Internal.BalanceTx.Collateral
6567
( addTxCollateral
6668
, addTxCollateralReturn
6769
)
68-
import Ctl.Internal.BalanceTx.Collateral.Select (selectCollateral)
70+
import Ctl.Internal.BalanceTx.Collateral.Select
71+
( minRequiredCollateral
72+
, selectCollateral
73+
) as Collateral
6974
import Ctl.Internal.BalanceTx.Constraints
7075
( BalanceTxConstraintsBuilder
7176
, _collateralUtxos
@@ -137,7 +142,7 @@ import Data.Array.NonEmpty
137142
import Data.Array.NonEmpty as NEA
138143
import Data.Bitraversable (ltraverse)
139144
import Data.Either (Either, hush, note)
140-
import Data.Foldable (fold, foldMap, foldr, length, null, sum)
145+
import Data.Foldable (foldMap, foldr, length, null, sum)
141146
import Data.Lens (view)
142147
import Data.Lens.Getter ((^.))
143148
import Data.Lens.Setter ((%~), (.~), (?~))
@@ -209,11 +214,6 @@ balanceTxWithConstraints transaction extraUtxos constraintsBuilder =
209214
<#> traverse (note CouldNotGetUtxos)
210215
>>> map (foldr Map.union Map.empty) -- merge all utxos into one map
211216

212-
unbalancedCollTx <- transactionWithNetworkId >>=
213-
if Array.null (transaction ^. _witnessSet <<< _redeemers)
214-
-- Don't set collateral if tx doesn't contain phase-2 scripts:
215-
then pure
216-
else setTransactionCollateral changeAddress
217217
let
218218
allUtxos :: UtxoMap
219219
allUtxos =
@@ -223,6 +223,12 @@ balanceTxWithConstraints transaction extraUtxos constraintsBuilder =
223223

224224
availableUtxos <- liftContract $ filterLockedUtxos allUtxos
225225

226+
unbalancedCollTx <- transactionWithNetworkId >>=
227+
if Array.null (transaction ^. _witnessSet <<< _redeemers)
228+
-- Don't set collateral if tx doesn't contain phase-2 scripts:
229+
then pure
230+
else setTransactionCollateral changeAddress availableUtxos
231+
226232
Logger.info (pprintUtxoMap allUtxos) "balanceTxWithConstraints: all UTxOs"
227233
Logger.info (pprintUtxoMap availableUtxos)
228234
"balanceTxWithConstraints: available UTxOs"
@@ -253,8 +259,9 @@ balanceTxWithConstraints transaction extraUtxos constraintsBuilder =
253259
(transaction ^. _body <<< _networkId)
254260
pure (transaction # _body <<< _networkId ?~ networkId)
255261

256-
setTransactionCollateral :: Address -> Transaction -> BalanceTxM Transaction
257-
setTransactionCollateral changeAddr transaction = do
262+
setTransactionCollateral
263+
:: Address -> UtxoMap -> Transaction -> BalanceTxM Transaction
264+
setTransactionCollateral changeAddr availableUtxos transaction = do
258265
nonSpendableSet <- asksConstraints _nonSpendableInputs
259266
mbCollateralUtxos <- asksConstraints _collateralUtxos
260267
-- We must filter out UTxOs that are set as non-spendable in the balancer
@@ -272,21 +279,42 @@ setTransactionCollateral changeAddr transaction = do
272279
when (not $ Array.null filteredUtxos) do
273280
logWarn' $ pprintTagSet
274281
"Some of the collateral UTxOs returned by the wallet were marked as non-spendable and ignored"
275-
(pprintUtxoMap (TransactionUnspentOutputs.toUtxoMap filteredUtxos))
276-
pure spendableUtxos
282+
(pprintUtxoMap (TransactionUnspentOutput.toUtxoMap filteredUtxos))
283+
let
284+
collVal =
285+
foldMap (Val.fromValue <<< view (_output <<< _amount))
286+
spendableUtxos
287+
minRequiredCollateral =
288+
BigNum.toBigInt $
289+
unwrap Collateral.minRequiredCollateral
290+
if (Val.getCoin collVal < minRequiredCollateral) then do
291+
logWarn' $ pprintTagSet
292+
"Filtered collateral UTxOs do not cover the minimum required \
293+
\collateral, reselecting collateral using CTL algorithm."
294+
(pprintUtxoMap (TransactionUnspentOutput.toUtxoMap spendableUtxos))
295+
selectCollateral availableUtxos
296+
else pure spendableUtxos
277297
-- otherwise, get all the utxos, filter out unspendable, and select
278298
-- collateral using internal algo, that is also used in KeyWallet
279-
Just utxoMap -> do
280-
ProtocolParameters params <- liftContract getProtocolParameters
281-
let
282-
maxCollateralInputs = UInt.toInt $ params.maxCollateralInputs
283-
mbCollateral =
284-
Array.fromFoldable <$>
285-
selectCollateral params.coinsPerUtxoByte maxCollateralInputs utxoMap
286-
liftEither $ note (InsufficientCollateralUtxos utxoMap) mbCollateral
299+
Just utxoMap -> selectCollateral utxoMap
287300
addTxCollateralReturn collateral (addTxCollateral collateral transaction)
288301
changeAddr
289302

303+
-- | Select collateral from the provided utxos using internal CTL
304+
-- | collateral selection algorithm.
305+
selectCollateral :: UtxoMap -> BalanceTxM (Array TransactionUnspentOutput)
306+
selectCollateral utxos = do
307+
pparams <- unwrap <$> liftContract getProtocolParameters
308+
let
309+
maxCollateralInputs = UInt.toInt $ pparams.maxCollateralInputs
310+
mbCollateral =
311+
Array.fromFoldable <$> Collateral.selectCollateral
312+
pparams.coinsPerUtxoByte
313+
maxCollateralInputs
314+
utxos
315+
liftEither $ note (InsufficientCollateralUtxos utxos)
316+
mbCollateral
317+
290318
--------------------------------------------------------------------------------
291319
-- Balancing Algorithm
292320
--------------------------------------------------------------------------------
@@ -346,11 +374,11 @@ runBalancer p = do
346374
isCip30 <- liftContract $ isCip30Wallet
347375
-- Get collateral inputs to mark them as unspendable.
348376
-- Some CIP-30 wallets don't allow to sign Txs that spend it.
349-
nonSpendableCollateralInputs <-
350-
if isCip30 then
351-
liftContract $ Wallet.getWalletCollateral <#>
352-
fold >>> map (unwrap >>> _.input) >>> Set.fromFoldable
353-
else mempty
377+
let
378+
nonSpendableCollateralInputs =
379+
if isCip30 then
380+
Set.fromFoldable $ p.transaction ^. _body <<< _collateral
381+
else mempty
354382
asksConstraints Constraints._nonSpendableInputs <#>
355383
append nonSpendableCollateralInputs >>>
356384
\nonSpendableInputs ->

0 commit comments

Comments
 (0)