-
Notifications
You must be signed in to change notification settings - Fork 213
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 tx max size & estimator to figure out the right input upper-bound in coin selection #536
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,8 +46,12 @@ largestFirst | |
-> ExceptT (ErrCoinSelection e) m (CoinSelection, UTxO) | ||
largestFirst opt outs utxo = do | ||
let descending = NE.toList . NE.sortBy (flip $ comparing coin) | ||
let n = fromIntegral $ maximumNumberOfInputs opt | ||
let nLargest = take n . L.sortBy (flip $ comparing (coin . snd)) . Map.toList . getUTxO | ||
let nOuts = fromIntegral $ NE.length outs | ||
let maxN = fromIntegral $ maximumNumberOfInputs opt (fromIntegral nOuts) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
let nLargest = take maxN | ||
. L.sortBy (flip $ comparing (coin . snd)) | ||
. Map.toList | ||
. getUTxO | ||
let guard = except . left ErrInvalidSelection . validate opt | ||
|
||
case foldM atLeast (nLargest utxo, mempty) (descending outs) of | ||
|
@@ -57,18 +61,17 @@ largestFirst opt outs utxo = do | |
let moneyRequested = sum $ (getCoin . coin) <$> (descending outs) | ||
let utxoBalance = fromIntegral $ balance utxo | ||
let nUtxo = fromIntegral $ L.length $ (Map.toList . getUTxO) utxo | ||
let nOuts = fromIntegral $ NE.length outs | ||
|
||
when (utxoBalance < moneyRequested) | ||
$ throwE $ ErrNotEnoughMoney utxoBalance moneyRequested | ||
|
||
when (nUtxo < nOuts) | ||
$ throwE $ ErrUtxoNotEnoughFragmented nUtxo nOuts | ||
|
||
when (fromIntegral n > nUtxo) | ||
when (fromIntegral maxN > nUtxo) | ||
$ throwE ErrInputsDepleted | ||
|
||
throwE $ ErrMaximumInputsReached (fromIntegral n) | ||
throwE $ ErrMaximumInputsReached (fromIntegral maxN) | ||
|
||
-- Selecting coins to cover at least the specified value | ||
-- The details of the algorithm are following: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,12 +116,14 @@ random | |
-> ExceptT (ErrCoinSelection e) m (CoinSelection, UTxO) | ||
random opt outs utxo = do | ||
let descending = NE.toList . NE.sortBy (flip $ comparing coin) | ||
let nOuts = fromIntegral $ NE.length outs | ||
let maxN = fromIntegral $ maximumNumberOfInputs opt nOuts | ||
randomMaybe <- lift $ runMaybeT $ | ||
foldM makeSelection (opt, utxo, []) (descending outs) | ||
foldM makeSelection (maxN, utxo, []) (descending outs) | ||
case randomMaybe of | ||
Just (opt', utxo', res) -> do | ||
Just (maxN', utxo', res) -> do | ||
(_, sel, remUtxo) <- lift $ | ||
foldM improveTxOut (opt', mempty, utxo') (reverse res) | ||
foldM improveTxOut (maxN', mempty, utxo') (reverse res) | ||
guard sel $> (sel, remUtxo) | ||
Nothing -> | ||
largestFirst opt outs utxo | ||
|
@@ -130,14 +132,14 @@ random opt outs utxo = do | |
|
||
-- | Perform a random selection on a given output, without improvement. | ||
makeSelection | ||
:: forall m e. MonadRandom m | ||
=> (CoinSelectionOptions e, UTxO, [([(TxIn, TxOut)], TxOut)]) | ||
:: forall m. MonadRandom m | ||
=> (Word64, UTxO, [([(TxIn, TxOut)], TxOut)]) | ||
-> TxOut | ||
-> MaybeT m (CoinSelectionOptions e, UTxO, [([(TxIn, TxOut)], TxOut)]) | ||
makeSelection (CoinSelectionOptions maxNumInputs fn, utxo0, selection) txout = do | ||
-> MaybeT m (Word64, UTxO, [([(TxIn, TxOut)], TxOut)]) | ||
makeSelection (maxNumInputs, utxo0, selection) txout = do | ||
(inps, utxo1) <- coverRandomly ([], utxo0) | ||
return | ||
( CoinSelectionOptions (maxNumInputs - fromIntegral (L.length inps)) fn | ||
( maxNumInputs - fromIntegral (L.length inps) | ||
, utxo1 | ||
, (inps, txout) : selection | ||
) | ||
|
@@ -156,14 +158,14 @@ makeSelection (CoinSelectionOptions maxNumInputs fn, utxo0, selection) txout = d | |
|
||
-- | Perform an improvement to random selection on a given output. | ||
improveTxOut | ||
:: forall m e. MonadRandom m | ||
=> (CoinSelectionOptions e, CoinSelection, UTxO) | ||
:: forall m. MonadRandom m | ||
=> (Word64, CoinSelection, UTxO) | ||
-> ([(TxIn, TxOut)], TxOut) | ||
-> m (CoinSelectionOptions e, CoinSelection, UTxO) | ||
improveTxOut (opt0, selection, utxo0) (inps0, txout) = do | ||
(opt, inps, utxo) <- improve (opt0, inps0, utxo0) | ||
-> m (Word64, CoinSelection, UTxO) | ||
improveTxOut (maxN0, selection, utxo0) (inps0, txout) = do | ||
(maxN, inps, utxo) <- improve (maxN0, inps0, utxo0) | ||
return | ||
( opt | ||
( maxN | ||
, selection <> CoinSelection | ||
{ inputs = inps | ||
, outputs = [txout] | ||
|
@@ -175,22 +177,22 @@ improveTxOut (opt0, selection, utxo0) (inps0, txout) = do | |
target = mkTargetRange txout | ||
|
||
improve | ||
:: forall m e. MonadRandom m | ||
=> (CoinSelectionOptions e, [(TxIn, TxOut)], UTxO) | ||
-> m (CoinSelectionOptions e, [(TxIn, TxOut)], UTxO) | ||
improve (opt@(CoinSelectionOptions maxN fn), inps, utxo) | ||
:: forall m. MonadRandom m | ||
=> (Word64, [(TxIn, TxOut)], UTxO) | ||
-> m (Word64, [(TxIn, TxOut)], UTxO) | ||
improve (maxN, inps, utxo) | ||
| maxN >= 1 && balance' inps < targetAim target = do | ||
runMaybeT (pickRandomT utxo) >>= \case | ||
Nothing -> | ||
return (opt, inps, utxo) | ||
return (maxN, inps, utxo) | ||
Just (io, utxo') | isImprovement io inps -> do | ||
let inps' = io : inps | ||
let opt' = CoinSelectionOptions (maxN - 1) fn | ||
improve (opt', inps', utxo') | ||
let maxN' = maxN - 1 | ||
improve (maxN', inps', utxo') | ||
Just _ -> | ||
return (opt, inps, utxo) | ||
return (maxN, inps, utxo) | ||
| otherwise = | ||
return (opt, inps, utxo) | ||
return (maxN, inps, utxo) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that most of the changes in this file are because the type for the |
||
isImprovement :: (TxIn, TxOut) -> [(TxIn, TxOut)] -> Bool | ||
isImprovement io selected = | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯