diff --git a/lib/core/src/Cardano/Wallet.hs b/lib/core/src/Cardano/Wallet.hs index 76065d9aeb0..0ba0a647c63 100644 --- a/lib/core/src/Cardano/Wallet.hs +++ b/lib/core/src/Cardano/Wallet.hs @@ -102,6 +102,7 @@ module Cardano.Wallet -- ** Payment , getTxExpiry , selectAssets + , readWalletUTxOIndex , selectAssetsNoOutputs , assignChangeAddresses , selectionToUnsignedTx @@ -1243,6 +1244,18 @@ selectionToUnsignedTx sel s = amount = view #coin bundle assets = view #tokens bundle +-- | Read a wallet checkpoint and index its UTxO, for 'selectAssets' and +-- 'selectAssetsNoOutputs'. +readWalletUTxOIndex + :: forall ctx s k. HasDBLayer s k ctx + => ctx + -> WalletId + -> ExceptT ErrNoSuchWallet IO (UTxOIndex, Wallet s, Set Tx) +readWalletUTxOIndex ctx wid = do + (cp, _, pending) <- readWallet @ctx @s @k ctx wid + let utxo = UTxOIndex.fromUTxO $ availableUTxO @s pending cp + return (utxo, cp, pending) + selectAssetsNoOutputs :: forall ctx s k result. ( HasTransactionLayer k ctx @@ -1252,10 +1265,11 @@ selectAssetsNoOutputs ) => ctx -> WalletId + -> (UTxOIndex, Wallet s, Set Tx) -> TransactionCtx -> (s -> SelectionResult TokenBundle -> result) -> ExceptT ErrSelectAssets IO result -selectAssetsNoOutputs ctx wid tx transform = do +selectAssetsNoOutputs ctx wid wal tx transform = do -- NOTE: -- Could be made nicer by allowing 'performSelection' to run with no target -- outputs, but to satisfy a minimum Ada target. @@ -1269,7 +1283,7 @@ selectAssetsNoOutputs ctx wid tx transform = do let dummyAddress = Address "" let dummyOutput = TxOut dummyAddress (TokenBundle.fromCoin deposit) let outs = dummyOutput :| [] - selectAssets @ctx @s @k ctx wid tx outs $ \s sel -> transform s $ sel + selectAssets @ctx @s @k ctx wal tx outs $ \s sel -> transform s $ sel { outputsCovered = mempty , changeGenerated = let @@ -1302,34 +1316,26 @@ selectAssets :: forall ctx s k result. ( HasTransactionLayer k ctx , HasLogger WalletLog ctx - , HasDBLayer s k ctx , HasNetworkLayer ctx ) => ctx - -> WalletId + -> (UTxOIndex, Wallet s, Set Tx) -> TransactionCtx -> NonEmpty TxOut -> (s -> SelectionResult TokenBundle -> result) -> ExceptT ErrSelectAssets IO result -selectAssets ctx wid tx outs transform = do - (cp, _, pending) <- withExceptT ErrSelectAssetsNoSuchWallet $ - readWallet @ctx @s @k ctx wid - let s = getState cp - - guardWithdrawal pending +selectAssets ctx (utxo, cp, pending) tx outs transform = do + guardPendingWithdrawal pp <- liftIO $ currentProtocolParameters nl - - let utxo :: UTxOIndex - utxo = UTxOIndex.fromUTxO $ availableUTxO @s pending cp - liftIO $ traceWith tr $ MsgSelectionStart utxo outs sel <- performSelection (calcMinimumCoinValue tl pp) (calcMinimumCost tl pp tx) (initSelectionCriteria tl pp tx utxo outs) liftIO $ traceWith tr $ MsgSelectionDone sel - withExceptT ErrSelectAssetsSelectionError $ except (transform s <$> sel) + withExceptT ErrSelectAssetsSelectionError $ except $ + transform (getState cp) <$> sel where nl = ctx ^. networkLayer tl = ctx ^. transactionLayer @k @@ -1340,8 +1346,8 @@ selectAssets ctx wid tx outs transform = do -- withdrawal is executed, the reward pot is empty. So, to prevent two -- transactions with withdrawals to go through (which will inevitably cause -- one of them to never be inserted), we warn users early on about it. - guardWithdrawal :: Set Tx -> ExceptT ErrSelectAssets IO () - guardWithdrawal pending = do + guardPendingWithdrawal :: ExceptT ErrSelectAssets IO () + guardPendingWithdrawal = case Set.lookupMin $ Set.filter hasWithdrawal pending of Just pendingWithdrawal | withdrawalToCoin (txWithdrawal tx) /= Coin 0 -> throwE $ ErrSelectAssetsAlreadyWithdrawing pendingWithdrawal diff --git a/lib/core/src/Cardano/Wallet/Api/Server.hs b/lib/core/src/Cardano/Wallet/Api/Server.hs index ec3db189c5f..65ae4097972 100644 --- a/lib/core/src/Cardano/Wallet/Api/Server.hs +++ b/lib/core/src/Cardano/Wallet/Api/Server.hs @@ -1196,8 +1196,9 @@ selectCoins ctx genChange (ApiT wid) body = do let transform = \s sel -> W.assignChangeAddresses genChange sel s & uncurry W.selectionToUnsignedTx + w <- liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid utx <- liftHandler - $ W.selectAssets @_ @s @k wrk wid txCtx outs transform + $ W.selectAssets @_ @s @k wrk w txCtx outs transform pure $ mkApiCoinSelection [] Nothing utx @@ -1239,8 +1240,9 @@ selectCoinsForJoin ctx knownPools getPoolStatus pid wid = do let transform = \s sel -> W.assignChangeAddresses (delegationAddress @n) sel s & uncurry W.selectionToUnsignedTx + wal <- liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid utx <- liftHandler - $ W.selectAssetsNoOutputs @_ @s @k wrk wid txCtx transform + $ W.selectAssetsNoOutputs @_ @s @k wrk wid wal txCtx transform (_, path) <- liftHandler $ W.readRewardAccount @_ @s @k @n wrk wid @@ -1263,8 +1265,7 @@ selectCoinsForQuit -> Handler (Api.ApiCoinSelection n) selectCoinsForQuit ctx (ApiT wid) = do withWorkerCtx ctx wid liftE liftE $ \wrk -> do - action <- liftHandler - $ W.quitStakePool @_ @s @k @n wrk wid + action <- liftHandler $ W.quitStakePool @_ @s @k @n wrk wid (wdrl, _mkRwdAcct) <- mkRewardAccountBuilder @_ @s @k @n ctx wid Nothing let txCtx = defaultTransactionCtx @@ -1275,10 +1276,10 @@ selectCoinsForQuit ctx (ApiT wid) = do let transform = \s sel -> W.assignChangeAddresses (delegationAddress @n) sel s & uncurry W.selectionToUnsignedTx + wal <- liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid utx <- liftHandler - $ W.selectAssetsNoOutputs @_ @s @k wrk wid txCtx transform - (_, path) <- liftHandler - $ W.readRewardAccount @_ @s @k @n wrk wid + $ W.selectAssetsNoOutputs @_ @s @k wrk wid wal txCtx transform + (_, path) <- liftHandler $ W.readRewardAccount @_ @s @k @n wrk wid pure $ mkApiCoinSelection [] (Just (action, path)) utx @@ -1435,8 +1436,9 @@ postTransaction ctx genChange (ApiT wid) body = do } (sel, tx, txMeta, txTime) <- withWorkerCtx ctx wid liftE liftE $ \wrk -> do + w <- liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid sel <- liftHandler - $ W.selectAssets @_ @s @k wrk wid txCtx outs (const Prelude.id) + $ W.selectAssets @_ @s @k wrk w txCtx outs (const Prelude.id) (tx, txMeta, txTime, sealedTx) <- liftHandler $ W.signTransaction @_ @s @k wrk wid genChange mkRwdAcct pwd txCtx sel liftHandler @@ -1540,7 +1542,8 @@ postTransactionFee ctx (ApiT wid) body = do , txMetadata = getApiT <$> body ^. #metadata } withWorkerCtx ctx wid liftE liftE $ \wrk -> do - let runSelection = W.selectAssets @_ @s @k wrk wid txCtx outs getFee + w <- liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid + let runSelection = W.selectAssets @_ @s @k wrk w txCtx outs getFee where outs = coerceCoin <$> body ^. #payments getFee = const (selectionDelta TokenBundle.getCoin) liftHandler $ mkApiFee Nothing <$> W.estimateFee runSelection @@ -1589,9 +1592,10 @@ joinStakePool ctx knownPools getPoolStatus apiPoolId (ApiT wid) body = do , txTimeToLive = ttl , txDelegationAction = Just action } - + wal <- liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid sel <- liftHandler - $ W.selectAssetsNoOutputs @_ @s @k wrk wid txCtx (const Prelude.id) + $ W.selectAssetsNoOutputs @_ @s @k wrk wid wal txCtx + $ const Prelude.id (tx, txMeta, txTime, sealedTx) <- liftHandler $ W.signTransaction @_ @s @k wrk wid genChange mkRwdAcct pwd txCtx sel liftHandler @@ -1625,14 +1629,16 @@ delegationFee -> Handler ApiFee delegationFee ctx (ApiT wid) = do withWorkerCtx ctx wid liftE liftE $ \wrk -> liftHandler $ do + w <- withExceptT ErrSelectAssetsNoSuchWallet $ + W.readWalletUTxOIndex @_ @s @k wrk wid deposit <- W.calcMinimumDeposit @_ @s @k wrk wid - mkApiFee (Just deposit) <$> W.estimateFee (runSelection wrk deposit) + mkApiFee (Just deposit) <$> W.estimateFee (runSelection wrk deposit w) where txCtx :: TransactionCtx txCtx = defaultTransactionCtx - runSelection wrk deposit = - W.selectAssetsNoOutputs @_ @s @k wrk wid txCtx calcFee + runSelection wrk deposit wal = + W.selectAssetsNoOutputs @_ @s @k wrk wid wal txCtx calcFee where calcFee _ = Coin.distance deposit . selectionDelta TokenBundle.getCoin @@ -1669,8 +1675,10 @@ quitStakePool ctx (ApiT wid) body = do , txDelegationAction = Just action } + wal <- liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid sel <- liftHandler - $ W.selectAssetsNoOutputs @_ @s @k wrk wid txCtx (const Prelude.id) + $ W.selectAssetsNoOutputs @_ @s @k wrk wid wal txCtx + $ const Prelude.id (tx, txMeta, txTime, sealedTx) <- liftHandler $ W.signTransaction @_ @s @k wrk wid genChange mkRwdAcct pwd txCtx sel liftHandler diff --git a/lib/shelley/bench/Restore.hs b/lib/shelley/bench/Restore.hs index d97e1b6eac7..f6c0dc5642b 100644 --- a/lib/shelley/bench/Restore.hs +++ b/lib/shelley/bench/Restore.hs @@ -454,7 +454,8 @@ benchmarksRnd _ w wid wname benchname restoreTime = do let out = TxOut (dummyAddress @n) (TokenBundle.fromCoin $ Coin 1) let txCtx = defaultTransactionCtx let getFee = const (selectionDelta TokenBundle.getCoin) - let runSelection = W.selectAssets @_ @s @k w wid txCtx (out :| []) getFee + wal <- unsafeRunExceptT $ W.readWalletUTxOIndex @_ @s @k w wid + let runSelection = W.selectAssets @_ @s @k w wal txCtx (out :| []) getFee runExceptT $ withExceptT show $ W.estimateFee runSelection oneAddress <- genAddresses 1 cp @@ -543,7 +544,8 @@ benchmarksSeq _ w wid _wname benchname restoreTime = do let out = TxOut (dummyAddress @n) (TokenBundle.fromCoin $ Coin 1) let txCtx = defaultTransactionCtx let getFee = const (selectionDelta TokenBundle.getCoin) - let runSelection = W.selectAssets @_ @s @k w wid txCtx (out :| []) getFee + wal <- unsafeRunExceptT $ W.readWalletUTxOIndex w wid + let runSelection = W.selectAssets @_ @s @k w wal txCtx (out :| []) getFee runExceptT $ withExceptT show $ W.estimateFee runSelection let walletOverview = WalletOverview{utxo,addresses,transactions}