From 489f5b8812c0dde81583e5cf871ce912d960f696 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 12 Jul 2019 16:31:09 +0200 Subject: [PATCH 1/2] add 'txMaxSize' to known blockchain parameters --- exe/wallet/http-bridge/Main.hs | 26 +++++++++++-------- lib/core/src/Cardano/Wallet.hs | 11 +++++--- lib/core/test/unit/Cardano/WalletSpec.hs | 13 ++++++---- .../Wallet/HttpBridge/Compatibility.hs | 8 +++++- lib/http-bridge/test/bench/Main.hs | 9 +++++-- .../test/integration/Cardano/WalletSpec.hs | 4 +-- lib/http-bridge/test/integration/Main.hs | 9 +++++-- .../Wallet/Jormungandr/Compatibility.hs | 13 ++++++++++ .../src/Cardano/Wallet/Jormungandr/Network.hs | 9 +++++-- lib/jormungandr/test/integration/Main.hs | 5 ++-- 10 files changed, 75 insertions(+), 32 deletions(-) diff --git a/exe/wallet/http-bridge/Main.hs b/exe/wallet/http-bridge/Main.hs index 45a8210a347..0f046ff7a82 100644 --- a/exe/wallet/http-bridge/Main.hs +++ b/exe/wallet/http-bridge/Main.hs @@ -65,21 +65,20 @@ import Cardano.Wallet.DaedalusIPC import Cardano.Wallet.DB ( DBLayer ) import Cardano.Wallet.HttpBridge.Compatibility - ( HttpBridge, Network (..), byronFeePolicy, byronSlotLength ) + ( HttpBridge + , Network (..) + , byronFeePolicy + , byronSlotLength + , byronTxMaxSize + ) import Cardano.Wallet.HttpBridge.Environment ( KnownNetwork (..) ) -import Cardano.Wallet.HttpBridge.Primitive.Types - ( Tx ) import Cardano.Wallet.Network ( ErrNetworkTip, NetworkLayer, defaultRetryPolicy, waitForConnection ) import Cardano.Wallet.Primitive.AddressDerivation ( KeyToAddress ) import Cardano.Wallet.Primitive.AddressDiscovery ( SeqState ) -import Cardano.Wallet.Primitive.Fee - ( FeePolicy ) -import Cardano.Wallet.Primitive.Types - ( Block ) import Cardano.Wallet.Version ( showVersion, version ) import Control.Applicative @@ -277,19 +276,24 @@ cmdServe = command "serve" $ info (helper <*> cmd) $ mempty -> DBLayer IO s t -> IO (WalletLayer s t) newWalletLayer (sb, tracer) db = do - (nl, block0, feePolicy) <- newNetworkLayer (sb, tracer) + (nl, bp) <- newNetworkLayer (sb, tracer) let tl = HttpBridge.newTransactionLayer @n - let bp = BlockchainParameters block0 feePolicy byronSlotLength Wallet.newWalletLayer tracer bp db nl tl newNetworkLayer :: (Switchboard Text, Trace IO Text) - -> IO (NetworkLayer t IO, Block Tx, FeePolicy) + -> IO (NetworkLayer t IO, BlockchainParameters t) newNetworkLayer (sb, tracer) = do nl <- HttpBridge.newNetworkLayer @n (getPort nodePort) waitForService @ErrNetworkTip "http-bridge" (sb, tracer) nodePort $ waitForConnection nl defaultRetryPolicy - return (nl, HttpBridge.block0, byronFeePolicy) + let bp = BlockchainParameters + { getGenesisBlock = HttpBridge.block0 + , getFeePolicy = byronFeePolicy + , getSlotLength = byronSlotLength + , getTxMaxSize = byronTxMaxSize + } + return (nl, bp) withDBLayer :: CM.Configuration diff --git a/lib/core/src/Cardano/Wallet.hs b/lib/core/src/Cardano/Wallet.hs index f1440cde663..f7bc652cf64 100644 --- a/lib/core/src/Cardano/Wallet.hs +++ b/lib/core/src/Cardano/Wallet.hs @@ -367,7 +367,11 @@ data BlockchainParameters t = BlockchainParameters { getGenesisBlock :: Block (Tx t) -- ^ Very first block , getFeePolicy :: FeePolicy + -- ^ Policy regarding transcation fee , getSlotLength :: SlotLength + -- ^ Length, in seconds, of a slot + , getTxMaxSize :: Quantity "byte" Word16 + -- ^ Maximum size of a transaction (soft or hard limit) } -- | Create a new instance of the wallet layer. @@ -379,10 +383,7 @@ newWalletLayer -> NetworkLayer t IO -> TransactionLayer t -> IO (WalletLayer s t) -newWalletLayer - tracer - (BlockchainParameters block0 feePolicy (SlotLength slotLength)) - db nw tl = do +newWalletLayer tracer bp db nw tl = do logDebugT $ "Wallet layer starting with: " <> "block0: "+| block0 |+ ", " <> "fee policy: "+|| feePolicy ||+"" @@ -404,6 +405,8 @@ newWalletLayer , listTransactions = _listTransactions } where + BlockchainParameters block0 feePolicy (SlotLength slotLength) txMaxSize = bp + logDebugT :: MonadIO m => Text -> m () logDebugT = liftIO . logDebug tracer diff --git a/lib/core/test/unit/Cardano/WalletSpec.hs b/lib/core/test/unit/Cardano/WalletSpec.hs index c64feae8ae7..5cad3221c41 100644 --- a/lib/core/test/unit/Cardano/WalletSpec.hs +++ b/lib/core/test/unit/Cardano/WalletSpec.hs @@ -372,7 +372,7 @@ setupFixture (wid, wname, wstate) = do db <- newDBLayer let nl = error "NetworkLayer" let tl = dummyTransactionLayer - let bp = BlockchainParameters block0 dummyPolicy dummySlotLength + let bp = BlockchainParameters block0 policy slotLength txMaxSize wl <- newWalletLayer @_ @DummyTarget nullTracer bp db nl tl res <- runExceptT $ createWallet wl wid wname wstate let wal = case res of @@ -380,11 +380,14 @@ setupFixture (wid, wname, wstate) = do Right walletId -> [walletId] pure $ WalletLayerFixture db wl wal where - dummyPolicy :: FeePolicy - dummyPolicy = LinearFee (Quantity 14) (Quantity 42) + policy :: FeePolicy + policy = LinearFee (Quantity 14) (Quantity 42) - dummySlotLength :: SlotLength - dummySlotLength = SlotLength $ secondsToDiffTime 1 + slotLength :: SlotLength + slotLength = SlotLength $ secondsToDiffTime 1 + + txMaxSize :: Quantity "byte" Word16 + txMaxSize = Quantity 8192 -- | A dummy transaction layer to see the effect of a root private key. It -- implements a fake signer that still produces sort of witnesses diff --git a/lib/http-bridge/src/Cardano/Wallet/HttpBridge/Compatibility.hs b/lib/http-bridge/src/Cardano/Wallet/HttpBridge/Compatibility.hs index 7c42a583287..8997aeb8868 100644 --- a/lib/http-bridge/src/Cardano/Wallet/HttpBridge/Compatibility.hs +++ b/lib/http-bridge/src/Cardano/Wallet/HttpBridge/Compatibility.hs @@ -20,6 +20,7 @@ module Cardano.Wallet.HttpBridge.Compatibility , block0 , byronFeePolicy , byronSlotLength + , byronTxMaxSize ) where import Prelude @@ -62,6 +63,8 @@ import Data.Text.Class ( TextDecodingError (..) ) import Data.Time.Clock ( secondsToDiffTime ) +import Data.Word + ( Word16 ) import qualified Cardano.Wallet.HttpBridge.Binary as CBOR import qualified Cardano.Wallet.HttpBridge.Primitive.Types as W @@ -159,7 +162,10 @@ block0 = Block byronFeePolicy :: FeePolicy byronFeePolicy = LinearFee (Quantity 155381) (Quantity 43.946) - -- | Hard-coded slot duration byronSlotLength :: SlotLength byronSlotLength = SlotLength $ secondsToDiffTime 20 + +-- | Hard-coded max transaction size +byronTxMaxSize :: Quantity "byte" Word16 +byronTxMaxSize = Quantity 8192 diff --git a/lib/http-bridge/test/bench/Main.hs b/lib/http-bridge/test/bench/Main.hs index 03beeef4412..075a4f448b5 100644 --- a/lib/http-bridge/test/bench/Main.hs +++ b/lib/http-bridge/test/bench/Main.hs @@ -20,7 +20,7 @@ import Cardano.Wallet import Cardano.Wallet.DB.Sqlite ( PersistState ) import Cardano.Wallet.HttpBridge.Compatibility - ( HttpBridge, block0, byronFeePolicy, byronSlotLength ) + ( HttpBridge, block0, byronFeePolicy, byronSlotLength, byronTxMaxSize ) import Cardano.Wallet.HttpBridge.Environment ( KnownNetwork (..), Network (..) ) import Cardano.Wallet.HttpBridge.Network @@ -212,7 +212,12 @@ bench_restoration _ (wid, wname, s) = withHttpBridge network $ \port -> do let tl = newTransactionLayer BlockHeader sl _ <- unsafeRunExceptT $ networkTip nw sayErr . fmt $ network ||+ " tip is at " +|| sl ||+ "" - let bp = BlockchainParameters block0 byronFeePolicy byronSlotLength + let bp = BlockchainParameters + { getGenesisBlock = block0 + , getFeePolicy = byronFeePolicy + , getSlotLength = byronSlotLength + , getTxMaxSize = byronTxMaxSize + } w <- newWalletLayer @_ @t nullTracer bp db nw tl wallet <- unsafeRunExceptT $ createWallet w wid wname s unsafeRunExceptT $ restoreWallet w wallet diff --git a/lib/http-bridge/test/integration/Cardano/WalletSpec.hs b/lib/http-bridge/test/integration/Cardano/WalletSpec.hs index 6fbd8d824a9..f33eb4aaf1d 100644 --- a/lib/http-bridge/test/integration/Cardano/WalletSpec.hs +++ b/lib/http-bridge/test/integration/Cardano/WalletSpec.hs @@ -15,7 +15,7 @@ import Cardano.Launcher import Cardano.Wallet ( BlockchainParameters (..), WalletLayer (..), newWalletLayer ) import Cardano.Wallet.HttpBridge.Compatibility - ( HttpBridge, block0, byronFeePolicy, byronSlotLength ) + ( HttpBridge, block0, byronFeePolicy, byronSlotLength, byronTxMaxSize ) import Cardano.Wallet.HttpBridge.Environment ( KnownNetwork (..), Network (..) ) import Cardano.Wallet.Primitive.AddressDerivation @@ -81,6 +81,6 @@ spec = do db <- MVar.newDBLayer nl <- HttpBridge.newNetworkLayer @'Testnet port let tl = HttpBridge.newTransactionLayer - let bp = BlockchainParameters block0 byronFeePolicy byronSlotLength + let bp = BlockchainParameters block0 byronFeePolicy byronSlotLength byronTxMaxSize (handle,) <$> (newWalletLayer @_ @(HttpBridge 'Testnet) nullTracer bp db nl tl) diff --git a/lib/http-bridge/test/integration/Main.hs b/lib/http-bridge/test/integration/Main.hs index 98754ee4bb2..5c65c5cac23 100644 --- a/lib/http-bridge/test/integration/Main.hs +++ b/lib/http-bridge/test/integration/Main.hs @@ -26,7 +26,7 @@ import Cardano.Wallet.Api.Server import Cardano.Wallet.DB.Sqlite ( SqliteContext ) import Cardano.Wallet.HttpBridge.Compatibility - ( HttpBridge, block0, byronFeePolicy, byronSlotLength ) + ( HttpBridge, block0, byronFeePolicy, byronSlotLength, byronTxMaxSize ) import Cardano.Wallet.HttpBridge.Environment ( Network (..) ) import Cardano.Wallet.Network @@ -264,7 +264,12 @@ main = do mvar <- newEmptyMVar thread <- forkIO $ do let tl = HttpBridge.newTransactionLayer - let bp = BlockchainParameters block0 byronFeePolicy byronSlotLength + let bp = BlockchainParameters + { getGenesisBlock = block0 + , getFeePolicy = byronFeePolicy + , getSlotLength = byronSlotLength + , getTxMaxSize = byronTxMaxSize + } wallet <- newWalletLayer nullTracer bp db nl tl let listen = fromMaybe (ListenOnPort defaultPort) mlisten Server.withListeningSocket listen $ \(port, socket) -> do diff --git a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Compatibility.hs b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Compatibility.hs index 79af6aea7a5..26b64f29c72 100644 --- a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Compatibility.hs +++ b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Compatibility.hs @@ -18,6 +18,7 @@ module Cardano.Wallet.Jormungandr.Compatibility Jormungandr , Network (..) , block0 + , softTxMaxSize -- * Node's Configuration , BaseUrl (..) @@ -63,8 +64,12 @@ import Data.Maybe ( fromJust, isJust ) import Data.Proxy ( Proxy (..) ) +import Data.Quantity + ( Quantity (..) ) import Data.Text.Class ( TextDecodingError (..) ) +import Data.Word + ( Word16 ) import Servant.Client.Core ( BaseUrl (..), Scheme (..) ) import System.FilePath @@ -90,6 +95,14 @@ block0 = BlockHeader , prevBlockHash = Hash (BS.replicate 32 0) } +-- | Jörmugandr's chain parameter doesn't include a transaction max size. The +-- actual hard-limit for the size is constrained by the binary format and +-- numbers used to represent the number of inputs and outputs (Word8), yet +-- there's also a soft-limit of 8kb which results in much smaller transactions +-- in the end. +softTxMaxSize :: Quantity "byte" Word16 +softTxMaxSize = Quantity 8192 + instance DefineTx (Jormungandr network) where type Tx (Jormungandr network) = Tx inputs = fmap fst . inputs diff --git a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Network.hs b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Network.hs index 0219d16f732..7e6c56369cf 100644 --- a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Network.hs +++ b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Network.hs @@ -50,7 +50,7 @@ import Cardano.Wallet.Jormungandr.Api import Cardano.Wallet.Jormungandr.Binary ( ConfigParam (..), Message (..), coerceBlock ) import Cardano.Wallet.Jormungandr.Compatibility - ( Jormungandr ) + ( Jormungandr, softTxMaxSize ) import Cardano.Wallet.Jormungandr.Primitive.Types ( Tx ) import Cardano.Wallet.Network @@ -267,7 +267,12 @@ mkJormungandrLayer mgr baseUrl = JormungandrLayer case (mpolicy,mduration) of ([policy],[duration]) -> - return $ BlockchainParameters (coerceBlock jblock) policy (SlotLength duration) + return $ BlockchainParameters + { getGenesisBlock = coerceBlock jblock + , getFeePolicy = policy + , getSlotLength = SlotLength duration + , getTxMaxSize = softTxMaxSize + } _ -> throwE $ ErrGetBlockchainParamsNoInitialPolicy params } diff --git a/lib/jormungandr/test/integration/Main.hs b/lib/jormungandr/test/integration/Main.hs index 19fd0893498..948d579f783 100644 --- a/lib/jormungandr/test/integration/Main.hs +++ b/lib/jormungandr/test/integration/Main.hs @@ -180,8 +180,7 @@ cardanoWalletServer cardanoWalletServer mlisten = do logConfig <- CM.empty tracer <- initTracer Info "serve" - (nl, bp@(BlockchainParameters _ feePolicy _) ) <- - newNetworkLayer jormungandrUrl block0H + (nl, bp) <- newNetworkLayer jormungandrUrl block0H (sqlCtx, db) <- Sqlite.newDBLayer @_ @network logConfig tracer Nothing mvar <- newEmptyMVar handle <- async $ do @@ -192,7 +191,7 @@ cardanoWalletServer mlisten = do let settings = Warp.defaultSettings & setBeforeMainLoop (putMVar mvar port) Server.start settings tracer socket wallet - (handle,,feePolicy,sqlCtx) <$> takeMVar mvar + (handle, , getFeePolicy bp, sqlCtx) <$> takeMVar mvar where jormungandrUrl :: BaseUrl jormungandrUrl = BaseUrl Http "localhost" 8080 "/api" From 5dc539e63f0c4c127aa1af0cb1b75a4f7f57f110 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 12 Jul 2019 16:32:55 +0200 Subject: [PATCH 2/2] Compute max number of allowed inputs from tx max size --- lib/core/src/Cardano/Wallet.hs | 5 +- .../Cardano/Wallet/Primitive/CoinSelection.hs | 6 ++- .../Primitive/CoinSelection/LargestFirst.hs | 13 +++-- .../Wallet/Primitive/CoinSelection/Random.hs | 48 ++++++++++--------- .../CoinSelection/LargestFirstSpec.hs | 6 +-- .../Primitive/CoinSelection/RandomSpec.hs | 10 ++-- .../Wallet/Primitive/CoinSelectionSpec.hs | 4 +- .../unit/Cardano/Wallet/Primitive/FeeSpec.hs | 2 +- lib/core/test/unit/Cardano/WalletSpec.hs | 2 +- .../Wallet/HttpBridge/TransactionSpec.hs | 2 +- .../Wallet/Jormungandr/TransactionSpec.hs | 2 +- 11 files changed, 55 insertions(+), 45 deletions(-) diff --git a/lib/core/src/Cardano/Wallet.hs b/lib/core/src/Cardano/Wallet.hs index f7bc652cf64..2a9c2b3e09d 100644 --- a/lib/core/src/Cardano/Wallet.hs +++ b/lib/core/src/Cardano/Wallet.hs @@ -168,6 +168,8 @@ import Data.Text.Class ( toText ) import Data.Time.Clock ( diffTimeToPicoseconds, getCurrentTime ) +import Data.Word + ( Word16 ) import Fmt ( Buildable, blockListF, pretty, (+|), (+||), (|+), (||+) ) @@ -635,10 +637,9 @@ newWalletLayer tracer bp db nw tl = do Transactions ---------------------------------------------------------------------------} - -- FIXME Compute the options based on the transaction's size / inputs coinSelOpts :: CoinSelectionOptions (ErrValidateSelection t) coinSelOpts = CoinSelectionOptions - { maximumNumberOfInputs = 10 + { maximumNumberOfInputs = estimateMaxNumberOfInputs tl txMaxSize , validate = validateSelection tl } diff --git a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs index 668b9cc46bb..85b4a50a05a 100644 --- a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs +++ b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs @@ -34,7 +34,7 @@ import Crypto.Number.Generate import Data.Vector.Mutable ( IOVector ) import Data.Word - ( Word64 ) + ( Word64, Word8 ) import Fmt ( Buildable (..), blockListF, blockListF', listF, nameF ) import GHC.Generics @@ -80,9 +80,11 @@ instance Buildable CoinSelection where data CoinSelectionOptions e = CoinSelectionOptions { maximumNumberOfInputs - :: Word64 + :: Word8 -> Word8 + -- ^ Maximum number of inputs allowed for a given number of outputs , validate :: CoinSelection -> Either e () + -- ^ Returns any backend-specific error regarding coin selection } deriving (Generic) data ErrCoinSelection e diff --git a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection/LargestFirst.hs b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection/LargestFirst.hs index 05fd5501171..29a53d1c0ea 100644 --- a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection/LargestFirst.hs +++ b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection/LargestFirst.hs @@ -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) + 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,7 +61,6 @@ 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 @@ -65,10 +68,10 @@ largestFirst opt outs utxo = do 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: diff --git a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection/Random.hs b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection/Random.hs index 7f6a4661e01..35baa6764b0 100644 --- a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection/Random.hs +++ b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection/Random.hs @@ -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) isImprovement :: (TxIn, TxOut) -> [(TxIn, TxOut)] -> Bool isImprovement io selected = diff --git a/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelection/LargestFirstSpec.hs b/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelection/LargestFirstSpec.hs index 97016fa59c9..07e7c330480 100644 --- a/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelection/LargestFirstSpec.hs +++ b/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelection/LargestFirstSpec.hs @@ -215,7 +215,7 @@ propDeterministic :: CoinSelProp -> Property propDeterministic (CoinSelProp utxo txOuts) = do - let opts = CoinSelectionOptions 100 noValidation + let opts = CoinSelectionOptions (const 100) noValidation let resultOne = runIdentity $ runExceptT $ largestFirst opts txOuts utxo let resultTwo = runIdentity $ runExceptT $ largestFirst opts txOuts utxo resultOne === resultTwo @@ -229,7 +229,7 @@ propAtLeast (CoinSelProp utxo txOuts) = prop (CoinSelection inps _ _) = L.length inps `shouldSatisfy` (>= NE.length txOuts) selection = runIdentity $ runExceptT $ - largestFirst (CoinSelectionOptions 100 noValidation) txOuts utxo + largestFirst (CoinSelectionOptions (const 100) noValidation) txOuts utxo propInputDecreasingOrder :: CoinSelProp @@ -247,4 +247,4 @@ propInputDecreasingOrder (CoinSelProp utxo txOuts) = (>= (getExtremumValue L.maximum utxo')) getExtremumValue f = f . map (getCoin . coin . snd) selection = runIdentity $ runExceptT $ - largestFirst (CoinSelectionOptions 100 noValidation) txOuts utxo + largestFirst (CoinSelectionOptions (const 100) noValidation) txOuts utxo diff --git a/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelection/RandomSpec.hs b/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelection/RandomSpec.hs index 23095c80fd2..981d5e96baa 100644 --- a/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelection/RandomSpec.hs +++ b/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelection/RandomSpec.hs @@ -250,9 +250,10 @@ propFragmentation drg (CoinSelProp utxo txOuts) = do prop (CoinSelection inps1 _ _, CoinSelection inps2 _ _) = L.length inps1 `shouldSatisfy` (>= L.length inps2) (selection1,_) = withDRG drg - (runExceptT $ random (CoinSelectionOptions 100 noValidation) txOuts utxo) + (runExceptT $ random opt txOuts utxo) selection2 = runIdentity $ runExceptT $ - largestFirst (CoinSelectionOptions 100 noValidation) txOuts utxo + largestFirst opt txOuts utxo + opt = CoinSelectionOptions (const 100) noValidation propErrors :: SystemDRG @@ -266,6 +267,7 @@ propErrors drg (CoinSelProp utxo txOuts) = do prop (err1, err2) = err1 === err2 (selection1,_) = withDRG drg - (runExceptT $ random (CoinSelectionOptions 1 noValidation) txOuts utxo) + (runExceptT $ random opt txOuts utxo) selection2 = runIdentity $ runExceptT $ - largestFirst (CoinSelectionOptions 1 noValidation) txOuts utxo + largestFirst opt txOuts utxo + opt = (CoinSelectionOptions (const 1) noValidation) diff --git a/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelectionSpec.hs b/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelectionSpec.hs index 01439ebc154..88e61c4de1d 100644 --- a/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelectionSpec.hs +++ b/lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelectionSpec.hs @@ -133,7 +133,7 @@ instance Buildable CoinSelProp where -- | A fixture for testing the coin selection data CoinSelectionFixture = CoinSelectionFixture - { maxNumOfInputs :: Word64 + { maxNumOfInputs :: Word8 -- ^ Maximum number of inputs that can be selected , validateSelection :: CoinSelection -> Either ErrValidation () -- ^ A extra validation function on the resulting selection @@ -178,7 +178,7 @@ coinSelectionUnitTest run lbl expected (CoinSelectionFixture n fn utxoF outsF) = (utxo,txOuts) <- setup result <- runExceptT $ do (CoinSelection inps outs chngs, _) <- - run (CoinSelectionOptions n fn) txOuts utxo + run (CoinSelectionOptions (const n) fn) txOuts utxo return $ CoinSelectionResult { rsInputs = map (getCoin . coin . snd) inps , rsChange = map getCoin chngs diff --git a/lib/core/test/unit/Cardano/Wallet/Primitive/FeeSpec.hs b/lib/core/test/unit/Cardano/Wallet/Primitive/FeeSpec.hs index 10ad6c909d5..c990e455cd1 100644 --- a/lib/core/test/unit/Cardano/Wallet/Primitive/FeeSpec.hs +++ b/lib/core/test/unit/Cardano/Wallet/Primitive/FeeSpec.hs @@ -521,7 +521,7 @@ genTxOut coins = do genSelection :: NonEmpty TxOut -> Gen CoinSelection genSelection outs = do - let opts = CS.CoinSelectionOptions 100 (const $ pure ()) + let opts = CS.CoinSelectionOptions (const 100) (const $ pure ()) utxo <- vectorOf (NE.length outs * 3) arbitrary >>= genUTxO case runIdentity $ runExceptT $ largestFirst opts outs utxo of Left _ -> genSelection outs diff --git a/lib/core/test/unit/Cardano/WalletSpec.hs b/lib/core/test/unit/Cardano/WalletSpec.hs index 5cad3221c41..eebcb32d182 100644 --- a/lib/core/test/unit/Cardano/WalletSpec.hs +++ b/lib/core/test/unit/Cardano/WalletSpec.hs @@ -109,7 +109,7 @@ import Data.Quantity import Data.Time.Clock ( secondsToDiffTime ) import Data.Word - ( Word32 ) + ( Word16, Word32 ) import GHC.Generics ( Generic ) import Test.Hspec diff --git a/lib/http-bridge/test/unit/Cardano/Wallet/HttpBridge/TransactionSpec.hs b/lib/http-bridge/test/unit/Cardano/Wallet/HttpBridge/TransactionSpec.hs index 131cb0aa445..0f633fd29d4 100644 --- a/lib/http-bridge/test/unit/Cardano/Wallet/HttpBridge/TransactionSpec.hs +++ b/lib/http-bridge/test/unit/Cardano/Wallet/HttpBridge/TransactionSpec.hs @@ -744,7 +744,7 @@ genTxOut coins = do genSelection :: NonEmpty TxOut -> Gen CoinSelection genSelection outs = do - let opts = CS.CoinSelectionOptions 100 (const $ Right ()) + let opts = CS.CoinSelectionOptions (const 100) (const $ Right ()) utxo <- vectorOf (NE.length outs * 3) arbitrary >>= genUTxO case runIdentity $ runExceptT $ largestFirst opts outs utxo of Left _ -> genSelection outs diff --git a/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs b/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs index 7ed66204812..5bac945ec50 100644 --- a/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs +++ b/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs @@ -165,7 +165,7 @@ genTxOut coins = do genSelection :: NonEmpty TxOut -> Gen CoinSelection genSelection outs = do - let opts = CS.CoinSelectionOptions 100 (const $ Right ()) + let opts = CS.CoinSelectionOptions (const 100) (const $ Right ()) utxo <- vectorOf (NE.length outs * 3) arbitrary >>= genUTxO case runIdentity $ runExceptT $ largestFirst opts outs utxo of Left _ -> genSelection outs