diff --git a/lib/byron/src/Cardano/Wallet/Byron/Api/Server.hs b/lib/byron/src/Cardano/Wallet/Byron/Api/Server.hs index 99ad8181fdd..9926e579179 100644 --- a/lib/byron/src/Cardano/Wallet/Byron/Api/Server.hs +++ b/lib/byron/src/Cardano/Wallet/Byron/Api/Server.hs @@ -153,7 +153,7 @@ server byron icarus ntp = transactions :: Server (Transactions n) transactions = (\_ _ _ -> throwError err501) - :<|> (\_ _ _ _ -> throwError err501) + :<|> (\_ _ _ _ _ -> throwError err501) :<|> (\_ _ _ -> throwError err501) :<|> (\_ _ -> throwError err501) :<|> (\_ _ -> throwError err501) @@ -246,8 +246,8 @@ server byron icarus ntp = ) :<|> (\wid r0 r1 s -> withLegacyLayer wid - (byron , listTransactions byron wid r0 r1 s) - (icarus, listTransactions icarus wid r0 r1 s) + (byron , listTransactions byron wid Nothing r0 r1 s) + (icarus, listTransactions icarus wid Nothing r0 r1 s) ) :<|> (\wid tx -> withLegacyLayer wid diff --git a/lib/byron/test/integration/Test/Integration/Byron/Scenario/API/Transactions.hs b/lib/byron/test/integration/Test/Integration/Byron/Scenario/API/Transactions.hs index a76e2c69f4f..ac2d358e7c7 100644 --- a/lib/byron/test/integration/Test/Integration/Byron/Scenario/API/Transactions.hs +++ b/lib/byron/test/integration/Test/Integration/Byron/Scenario/API/Transactions.hs @@ -711,12 +711,12 @@ scenario_TRANS_REG_1670 fixture = it title $ \ctx -> do -- ACTION rTxsFromDate <- request @[ApiTransaction n] ctx - (Link.listTransactions' @'Byron wSrc (Just start) Nothing Nothing) + (Link.listTransactions' @'Byron wSrc Nothing (Just start) Nothing Nothing) Default Empty rTxsAll <- request @[ApiTransaction n] ctx - (Link.listTransactions' @'Byron wSrc Nothing Nothing Nothing) + (Link.listTransactions' @'Byron wSrc Nothing Nothing Nothing Nothing) Default Empty diff --git a/lib/core-integration/src/Test/Integration/Framework/DSL.hs b/lib/core-integration/src/Test/Integration/Framework/DSL.hs index ce488e34e3c..86c172080bc 100644 --- a/lib/core-integration/src/Test/Integration/Framework/DSL.hs +++ b/lib/core-integration/src/Test/Integration/Framework/DSL.hs @@ -1275,6 +1275,7 @@ listTransactions ctx wallet mStart mEnd mOrder = do return txs where path = Link.listTransactions' @'Shelley wallet + Nothing (Iso8601Time <$> mStart) (Iso8601Time <$> mEnd) mOrder diff --git a/lib/core-integration/src/Test/Integration/Scenario/API/Byron/Transactions.hs b/lib/core-integration/src/Test/Integration/Scenario/API/Byron/Transactions.hs index 803f4e9ec23..947070beeae 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/API/Byron/Transactions.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/API/Byron/Transactions.hs @@ -221,6 +221,7 @@ spec = do let startTime = "2009-09-09T09:09:09Z" let endTime = "2001-01-01T01:01:01Z" let link = Link.listTransactions' @'Byron w + Nothing (either (const Nothing) Just $ fromText $ T.pack startTime) (either (const Nothing) Just $ fromText $ T.pack endTime) Nothing diff --git a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs index d15b5b1103f..c486dcb9e19 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs @@ -180,6 +180,7 @@ spec = do -- Verify Tx let link = Link.listTransactions' @'Shelley wSrc + Nothing Nothing Nothing (Just Descending) @@ -1082,6 +1083,7 @@ spec = do let startTime = "2009-09-09T09:09:09Z" let endTime = "2001-01-01T01:01:01Z" let link = Link.listTransactions' @'Shelley w + Nothing (either (const Nothing) Just $ fromText $ T.pack startTime) (either (const Nothing) Just $ fromText $ T.pack endTime) Nothing diff --git a/lib/core/src/Cardano/Wallet.hs b/lib/core/src/Cardano/Wallet.hs index 91b0bbc8f58..fd8562d6e4f 100644 --- a/lib/core/src/Cardano/Wallet.hs +++ b/lib/core/src/Cardano/Wallet.hs @@ -616,7 +616,7 @@ readWallet ctx wid = db & \DBLayer{..} -> mapExceptT atomically $ do let pk = PrimaryKey wid cp <- withNoSuchWallet wid $ readCheckpoint pk meta <- withNoSuchWallet wid $ readWalletMeta pk - pending <- lift $ readTxHistory pk Descending wholeRange (Just Pending) + pending <- lift $ readTxHistory pk Nothing Descending wholeRange (Just Pending) pure (cp, meta, Set.fromList (fromTransactionInfo <$> pending)) where db = ctx ^. dbLayer @s @k @@ -1012,7 +1012,7 @@ listAddresses listAddresses ctx wid normalize = db & \DBLayer{..} -> do (s, txs) <- mapExceptT atomically $ (,) <$> (getState <$> withNoSuchWallet wid (readCheckpoint primaryKey)) - <*> lift (readTxHistory primaryKey Descending wholeRange Nothing) + <*> lift (readTxHistory primaryKey Nothing Descending wholeRange Nothing) let maybeIsOurs (TxOut a _) = if fst (isOurs a s) then normalize s a else Nothing @@ -1640,13 +1640,15 @@ listTransactions :: forall ctx s k. HasDBLayer s k ctx => ctx -> WalletId + -> Maybe (Quantity "lovelace" Natural) + -- Inclusive minimum value of at least one withdrawal in each transaction -> Maybe UTCTime -- Inclusive minimum time bound. -> Maybe UTCTime -- Inclusive maximum time bound. -> SortOrder -> ExceptT ErrListTransactions IO [TransactionInfo] -listTransactions ctx wid mStart mEnd order = db & \DBLayer{..} -> do +listTransactions ctx wid mMinWithdrawal mStart mEnd order = db & \DBLayer{..} -> do let pk = PrimaryKey wid mapExceptT atomically $ do cp <- withExceptT ErrListTransactionsNoSuchWallet $ @@ -1656,7 +1658,7 @@ listTransactions ctx wid mStart mEnd order = db & \DBLayer{..} -> do mapExceptT liftIO (getSlotRange sp) >>= maybe (pure []) - (\r -> lift (readTxHistory pk order r Nothing)) + (\r -> lift (readTxHistory pk mMinWithdrawal order r Nothing)) where db = ctx ^. dbLayer @s @k diff --git a/lib/core/src/Cardano/Wallet/Api.hs b/lib/core/src/Cardano/Wallet/Api.hs index 04e5d407128..442079ca77c 100644 --- a/lib/core/src/Cardano/Wallet/Api.hs +++ b/lib/core/src/Cardano/Wallet/Api.hs @@ -123,6 +123,7 @@ import Cardano.Wallet.Api.Types , ApiWalletPassphrase , ByronWalletPutPassphraseData , Iso8601Time + , MinWithdrawal , PostExternalTransactionData , PostTransactionDataT , PostTransactionFeeDataT @@ -316,6 +317,7 @@ type CreateTransaction n = "wallets" type ListTransactions n = "wallets" :> Capture "walletId" (ApiT WalletId) :> "transactions" + :> QueryParam "minWithdrawal" MinWithdrawal :> QueryParam "start" Iso8601Time :> QueryParam "end" Iso8601Time :> QueryParam "order" (ApiT SortOrder) diff --git a/lib/core/src/Cardano/Wallet/Api/Client.hs b/lib/core/src/Cardano/Wallet/Api/Client.hs index 994c66632ec..0694b03fd5a 100644 --- a/lib/core/src/Cardano/Wallet/Api/Client.hs +++ b/lib/core/src/Cardano/Wallet/Api/Client.hs @@ -271,7 +271,7 @@ transactionClient = = client (Proxy @("v2" :> Proxy_)) in TransactionClient - { listTransactions = _listTransactions + { listTransactions = (`_listTransactions` Nothing) , postTransaction = \wid -> _postTransaction wid . coerce , postTransactionFee = \wid -> _postTransactionFee wid . coerce , postExternalTransaction = _postExternalTransaction diff --git a/lib/core/src/Cardano/Wallet/Api/Link.hs b/lib/core/src/Cardano/Wallet/Api/Link.hs index 3b2a4fb23af..e4df97c6962 100644 --- a/lib/core/src/Cardano/Wallet/Api/Link.hs +++ b/lib/core/src/Cardano/Wallet/Api/Link.hs @@ -94,6 +94,7 @@ import Cardano.Wallet.Api.Types , ApiTxId (ApiTxId) , ApiWithdrawRewards (..) , Iso8601Time + , MinWithdrawal (..) , WalletStyle (..) ) import Cardano.Wallet.Primitive.AddressDerivation @@ -114,6 +115,8 @@ import GHC.TypeLits ( Symbol ) import Network.HTTP.Types.Method ( Method ) +import Numeric.Natural + ( Natural ) import Servant.API ( (:>) , Capture @@ -344,7 +347,7 @@ listTransactions => w -> (Method, Text) listTransactions w = - listTransactions' @style w Nothing Nothing Nothing + listTransactions' @style w Nothing Nothing Nothing Nothing listTransactions' :: forall (style :: WalletStyle) w. @@ -352,16 +355,18 @@ listTransactions' , HasType (ApiT WalletId) w ) => w + -> Maybe Natural -> Maybe Iso8601Time -> Maybe Iso8601Time -> Maybe SortOrder -> (Method, Text) -listTransactions' w inf sup order = discriminate @style - (endpoint @(Api.ListTransactions Net) mkURL) - (endpoint @(Api.ListByronTransactions Net) mkURL) +listTransactions' w minWithdrawal inf sup order = discriminate @style + (endpoint @(Api.ListTransactions Net) + (\mk -> mk wid (MinWithdrawal <$> minWithdrawal) inf sup (ApiT <$> order))) + (endpoint @(Api.ListByronTransactions Net) + (\mk -> mk wid inf sup (ApiT <$> order))) where wid = w ^. typed @(ApiT WalletId) - mkURL mk = mk wid inf sup (ApiT <$> order) getTransactionFee :: forall style w. diff --git a/lib/core/src/Cardano/Wallet/Api/Server.hs b/lib/core/src/Cardano/Wallet/Api/Server.hs index 625a78f0522..031e4e0d5df 100644 --- a/lib/core/src/Cardano/Wallet/Api/Server.hs +++ b/lib/core/src/Cardano/Wallet/Api/Server.hs @@ -186,6 +186,7 @@ import Cardano.Wallet.Api.Types , ByronWalletPutPassphraseData (..) , Iso8601Time (..) , KnownDiscovery (..) + , MinWithdrawal (..) , PostExternalTransactionData (..) , PostTransactionData , PostTransactionFeeData @@ -1169,13 +1170,15 @@ listTransactions :: forall ctx s t k n. (ctx ~ ApiLayer s t k) => ctx -> ApiT WalletId + -> Maybe MinWithdrawal -> Maybe Iso8601Time -> Maybe Iso8601Time -> Maybe (ApiT SortOrder) -> Handler [ApiTransaction n] -listTransactions ctx (ApiT wid) mStart mEnd mOrder = do +listTransactions ctx (ApiT wid) mMinWithdrawal mStart mEnd mOrder = do txs <- withWorkerCtx ctx wid liftE liftE $ \wrk -> liftHandler $ W.listTransactions wrk wid + (Quantity . getMinWithdrawal <$> mMinWithdrawal) (getIso8601Time <$> mStart) (getIso8601Time <$> mEnd) (maybe defaultSortOrder getApiT mOrder) diff --git a/lib/core/src/Cardano/Wallet/Api/Types.hs b/lib/core/src/Cardano/Wallet/Api/Types.hs index 634e3357593..ba2f677493c 100644 --- a/lib/core/src/Cardano/Wallet/Api/Types.hs +++ b/lib/core/src/Cardano/Wallet/Api/Types.hs @@ -72,6 +72,7 @@ module Cardano.Wallet.Api.Types , ApiBlockReference (..) , ApiNetworkTip (..) , Iso8601Time (..) + , MinWithdrawal (..) , ApiNetworkParameters (..) , toApiNetworkParameters , ApiWalletDelegation (..) @@ -686,6 +687,16 @@ instance FromHttpApiData Iso8601Time where instance ToHttpApiData Iso8601Time where toUrlPiece = toText +newtype MinWithdrawal = MinWithdrawal + { getMinWithdrawal :: Natural + } deriving (Show) + +instance FromHttpApiData MinWithdrawal where + parseUrlPiece = bimap (T.pack . getTextDecodingError) MinWithdrawal . fromText + +instance ToHttpApiData MinWithdrawal where + toUrlPiece = toText . getMinWithdrawal + instance ToText NtpSyncingStatus where toText NtpSyncingStatusUnavailable = "unavailable" toText NtpSyncingStatusPending = "pending" diff --git a/lib/core/src/Cardano/Wallet/DB.hs b/lib/core/src/Cardano/Wallet/DB.hs index d3b038c65b7..bed55f84001 100644 --- a/lib/core/src/Cardano/Wallet/DB.hs +++ b/lib/core/src/Cardano/Wallet/DB.hs @@ -60,6 +60,8 @@ import Data.Quantity ( Quantity (..) ) import Data.Word ( Word32, Word64 ) +import Numeric.Natural + ( Natural ) import qualified Data.List as L @@ -219,6 +221,7 @@ data DBLayer m s k = forall stm. (MonadIO stm, MonadFail stm) => DBLayer , readTxHistory :: PrimaryKey WalletId + -> Maybe (Quantity "lovelace" Natural) -> SortOrder -> Range SlotId -> Maybe TxStatus diff --git a/lib/core/src/Cardano/Wallet/DB/MVar.hs b/lib/core/src/Cardano/Wallet/DB/MVar.hs index 204d2fb012a..86fc5688b56 100644 --- a/lib/core/src/Cardano/Wallet/DB/MVar.hs +++ b/lib/core/src/Cardano/Wallet/DB/MVar.hs @@ -129,14 +129,15 @@ newDBLayer = do , putTxHistory = \pk txh -> ExceptT $ do txh `deepseq` alterDB errNoSuchWallet db (mPutTxHistory pk txh) - , readTxHistory = \pk order range mstatus -> - readDB db (mReadTxHistory pk order range mstatus) + , readTxHistory = \pk minWithdrawal order range mstatus -> + readDB db (mReadTxHistory pk minWithdrawal order range mstatus) , getTx = \pk tid -> ExceptT $ alterDB errNoSuchWallet db (mCheckWallet pk) >>= \case Left err -> pure $ Left err Right _ -> do - txInfos <- readDB db (mReadTxHistory pk Descending wholeRange Nothing) + txInfos <- readDB db + (mReadTxHistory pk Nothing Descending wholeRange Nothing) let txPresent (TransactionInfo{..}) = txInfoId == tid case filter txPresent txInfos of [] -> pure $ Right Nothing diff --git a/lib/core/src/Cardano/Wallet/DB/Model.hs b/lib/core/src/Cardano/Wallet/DB/Model.hs index 836dd2968ce..5e1b554cc1d 100644 --- a/lib/core/src/Cardano/Wallet/DB/Model.hs +++ b/lib/core/src/Cardano/Wallet/DB/Model.hs @@ -65,11 +65,11 @@ module Cardano.Wallet.DB.Model import Prelude - import Cardano.Wallet.Primitive.Model ( Wallet, blockchainParameters, currentTip, utxo ) import Cardano.Wallet.Primitive.Types ( BlockHeader (blockHeight, slotId) + , Coin (..) , DelegationCertificate (..) , Direction (..) , EpochNo (..) @@ -114,6 +114,8 @@ import Data.Word ( Word32, Word64 ) import GHC.Generics ( Generic ) +import Numeric.Natural + ( Natural ) import qualified Data.Map.Strict as Map @@ -398,11 +400,13 @@ mPutTxHistory wid txList db@Database{wallets,txs} = mReadTxHistory :: forall wid s xprv. Ord wid => wid + -> Maybe (Quantity "lovelace" Natural) -> SortOrder -> Range SlotId -> Maybe TxStatus -> ModelOp wid s xprv [TransactionInfo] -mReadTxHistory wid order range mstatus db@(Database wallets txs) = (Right res, db) +mReadTxHistory wid minWithdrawal order range mstatus db@(Database wallets txs) = + (Right res, db) where res = fromMaybe mempty $ do wal <- Map.lookup wid wallets @@ -411,7 +415,7 @@ mReadTxHistory wid order range mstatus db@(Database wallets txs) = (Right res, d getTxs cp history = fmap (mkTransactionInfo cp) - $ filterTxHistory order range + $ filterTxHistory minWithdrawal order range $ catMaybes [ fmap (, meta) (Map.lookup tid txs) | (tid, meta) <- Map.toList history @@ -490,12 +494,14 @@ alterModel wid f db@Database{wallets,txs} = case f <$> Map.lookup wid wallets of -- | Apply optional filters on slotId and sort using the default sort order -- (first time/slotId, then by TxId) to a 'TxHistory'. filterTxHistory - :: SortOrder + :: Maybe (Quantity "lovelace" Natural) + -> SortOrder -> Range SlotId -> TxHistory -> TxHistory -filterTxHistory order range = - filter ((`isWithinRange` range) . (slotId :: TxMeta -> SlotId) . snd) +filterTxHistory minWithdrawal order range = + filter (filterWithdrawals minWithdrawal) + . filter ((`isWithinRange` range) . (slotId :: TxMeta -> SlotId) . snd) . (case order of Ascending -> reverse Descending -> id) @@ -504,6 +510,12 @@ filterTxHistory order range = where sortBySlot = sortOn (Down . (slotId :: TxMeta -> SlotId) . snd) sortByTxId = sortOn (txId . fst) + atLeast (Quantity inf) = + not . Map.null . Map.filter (> Coin (fromIntegral inf)) + filterWithdrawals = maybe + (const True) + (\inf -> atLeast inf . withdrawals . fst) + tip :: Wallet s -> SlotId tip = (slotId :: BlockHeader -> SlotId) . currentTip diff --git a/lib/core/src/Cardano/Wallet/DB/Sqlite.hs b/lib/core/src/Cardano/Wallet/DB/Sqlite.hs index 75ccb522e2c..50e1079f86e 100644 --- a/lib/core/src/Cardano/Wallet/DB/Sqlite.hs +++ b/lib/core/src/Cardano/Wallet/DB/Sqlite.hs @@ -178,6 +178,8 @@ import Database.Persist.Types ( PersistValue (PersistText), fromPersistValueText ) import Fmt ( pretty ) +import Numeric.Natural + ( Natural ) import System.Directory ( doesFileExist, listDirectory ) import System.FilePath @@ -687,8 +689,8 @@ newDBLayer trace defaultFieldValues mDatabaseFile = do putTxs txins txouts txws pure $ Right () - , readTxHistory = \(PrimaryKey wid) order range status -> do - selectTxHistory wid order $ catMaybes + , readTxHistory = \(PrimaryKey wid) minWithdrawal order range status -> do + selectTxHistory wid minWithdrawal order $ catMaybes [ (TxMetaSlot >=.) <$> W.inclusiveLowerBound range , (TxMetaSlot <=.) <$> W.inclusiveUpperBound range , (TxMetaStatus ==.) <$> status @@ -717,7 +719,7 @@ newDBLayer trace defaultFieldValues mDatabaseFile = do selectWallet wid >>= \case Nothing -> pure $ Left $ ErrNoSuchWallet wid Just _ -> do - metas <- selectTxHistory wid W.Descending + metas <- selectTxHistory wid Nothing W.Descending [ TxMetaTxId ==. (TxId tid) ] case metas of [] -> pure (Right Nothing) @@ -1299,15 +1301,30 @@ selectTxs = fmap concatUnzip . mapM select . chunksOf chunkSize selectTxHistory :: W.WalletId + -> Maybe (Quantity "lovelace" Natural) -> W.SortOrder -> [Filter TxMeta] -> SqlPersistT IO [W.TransactionInfo] -selectTxHistory wid order conditions = do +selectTxHistory wid minWithdrawal order conditions = do selectLatestCheckpoint wid >>= \case Nothing -> pure [] Just cp -> do + minWithdrawalFilter <- case minWithdrawal of + Nothing -> pure [] + Just inf -> do + let coin = W.Coin $ fromIntegral $ getQuantity inf + wdrls <- fmap entityVal + <$> selectList [ TxWithdrawalAmount >=. coin ] [] + pure [ TxMetaTxId <-. (txWithdrawalTxId <$> wdrls) ] + metas <- fmap entityVal <$> selectList - ((TxMetaWalletId ==. wid) : conditions) sortOpt + ( mconcat + [ [ TxMetaWalletId ==. wid ] + , minWithdrawalFilter + , conditions + ] + ) sortOpt + let txids = map txMetaTxId metas (ins, outs, ws) <- selectTxs txids diff --git a/lib/core/test/bench/db/Main.hs b/lib/core/test/bench/db/Main.hs index 5459c689dcc..c146d8f6db6 100644 --- a/lib/core/test/bench/db/Main.hs +++ b/lib/core/test/bench/db/Main.hs @@ -398,7 +398,7 @@ benchReadTxHistory -> DBLayerBench -> IO [TransactionInfo] benchReadTxHistory sortOrder (inf, sup) mstatus DBLayer{..} = - atomically $ readTxHistory testPk sortOrder range mstatus + atomically $ readTxHistory testPk Nothing sortOrder range mstatus where range = Range (fromFlatSlot epochLength <$> inf) diff --git a/lib/core/test/unit/Cardano/Wallet/DB/Arbitrary.hs b/lib/core/test/unit/Cardano/Wallet/DB/Arbitrary.hs index c6bb51e809e..284bd67f9c1 100644 --- a/lib/core/test/unit/Cardano/Wallet/DB/Arbitrary.hs +++ b/lib/core/test/unit/Cardano/Wallet/DB/Arbitrary.hs @@ -251,7 +251,7 @@ instance Arbitrary GenTxHistory where -- checkpoint's pending transactions of the same wallet. filter (not . isPending . snd) <$> arbitrary where - sortTxHistory = filterTxHistory Descending wholeRange + sortTxHistory = filterTxHistory Nothing Descending wholeRange instance Arbitrary MockChain where shrink (MockChain chain) = diff --git a/lib/core/test/unit/Cardano/Wallet/DB/Properties.hs b/lib/core/test/unit/Cardano/Wallet/DB/Properties.hs index 6819240fbca..7b0cddf0cbf 100644 --- a/lib/core/test/unit/Cardano/Wallet/DB/Properties.hs +++ b/lib/core/test/unit/Cardano/Wallet/DB/Properties.hs @@ -324,7 +324,7 @@ readTxHistoryF -> m (Identity GenTxHistory) readTxHistoryF DBLayer{..} wid = (Identity . GenTxHistory . fmap toTxHistory) - <$> atomically (readTxHistory wid Descending wholeRange Nothing) + <$> atomically (readTxHistory wid Nothing Descending wholeRange Nothing) putTxHistoryF :: DBLayer m s JormungandrKey @@ -360,7 +360,7 @@ sortedUnions :: Ord k => [(k, GenTxHistory)] -> [Identity GenTxHistory] sortedUnions = map (Identity . sort' . runIdentity) . unions where sort' = GenTxHistory - . filterTxHistory Descending wholeRange + . filterTxHistory Nothing Descending wholeRange . unGenTxHistory -- | Execute an action once per key @k@ present in the given list @@ -820,7 +820,7 @@ prop_rollbackTxHistory db@DBLayer{..} (InitialCheckpoint cp0) (GenTxHistory txs0 prop wid requestedPoint = do point <- run $ unsafeRunExceptT $ mapExceptT atomically $ rollbackTo wid requestedPoint txs <- run $ atomically $ fmap toTxHistory - <$> readTxHistory wid Descending wholeRange Nothing + <$> readTxHistory wid Nothing Descending wholeRange Nothing monitor $ counterexample $ "\n" <> "Actual Rollback Point:\n" <> (pretty point) monitor $ counterexample $ "\nOriginal tx history:\n" <> (txsF txs0) diff --git a/lib/core/test/unit/Cardano/Wallet/DB/SqliteSpec.hs b/lib/core/test/unit/Cardano/Wallet/DB/SqliteSpec.hs index b7b849b1adb..cdad0fbc57c 100644 --- a/lib/core/test/unit/Cardano/Wallet/DB/SqliteSpec.hs +++ b/lib/core/test/unit/Cardano/Wallet/DB/SqliteSpec.hs @@ -774,7 +774,7 @@ readTxHistory' -> Maybe TxStatus -> m [(Tx, TxMeta)] readTxHistory' DBLayer{..} a0 a1 a2 = - atomically . fmap (fmap toTxHistory) . readTxHistory a0 a1 a2 + atomically . fmap (fmap toTxHistory) . readTxHistory a0 Nothing a1 a2 readPrivateKey' :: DBLayer m s k @@ -856,13 +856,13 @@ getAvailableBalance :: DBLayer IO s k -> IO Word getAvailableBalance DBLayer{..} = do cp <- fmap (fromMaybe (error "nothing")) <$> atomically $ readCheckpoint testPk pend <- atomically $ fmap toTxHistory - <$> readTxHistory testPk Descending wholeRange (Just Pending) + <$> readTxHistory testPk Nothing Descending wholeRange (Just Pending) return $ fromIntegral $ availableBalance (Set.fromList $ map fst pend) cp getTxsInLedger :: DBLayer IO s k -> IO ([(Direction, Natural)]) getTxsInLedger DBLayer {..} = do pend <- atomically $ fmap toTxHistory - <$> readTxHistory testPk Descending wholeRange (Just InLedger) + <$> readTxHistory testPk Nothing Descending wholeRange (Just InLedger) return $ map (\(_, m) -> (direction m, getQuantity $ amount m)) pend {------------------------------------------------------------------------------- diff --git a/lib/core/test/unit/Cardano/Wallet/DB/StateMachine.hs b/lib/core/test/unit/Cardano/Wallet/DB/StateMachine.hs index 80dbed20a47..36221812295 100644 --- a/lib/core/test/unit/Cardano/Wallet/DB/StateMachine.hs +++ b/lib/core/test/unit/Cardano/Wallet/DB/StateMachine.hs @@ -171,6 +171,8 @@ import Data.Word ( Word64 ) import GHC.Generics ( Generic ) +import Numeric.Natural + ( Natural ) import System.Random ( getStdRandom, randomR ) import Test.QuickCheck @@ -293,7 +295,11 @@ data Cmd s wid | PutWalletMeta wid WalletMetadata | ReadWalletMeta wid | PutTxHistory wid TxHistory - | ReadTxHistory wid SortOrder (Range SlotId) (Maybe TxStatus) + | ReadTxHistory wid + (Maybe (Quantity "lovelace" Natural)) + SortOrder + (Range SlotId) + (Maybe TxStatus) | PutPrivateKey wid MPrivKey | ReadPrivateKey wid | PutProtocolParameters wid ProtocolParameters @@ -366,8 +372,8 @@ runMock = \case first (Resp . fmap StakeKeyStatus) . mIsStakeKeyRegistered wid PutTxHistory wid txs -> first (Resp . fmap Unit) . mPutTxHistory wid txs - ReadTxHistory wid order range status -> - first (Resp . fmap TxHistory) . mReadTxHistory wid order range status + ReadTxHistory wid minW order range status -> + first (Resp . fmap TxHistory) . mReadTxHistory wid minW order range status PutPrivateKey wid pk -> first (Resp . fmap Unit) . mPutPrivateKey wid pk ReadPrivateKey wid -> @@ -430,8 +436,8 @@ runIO db@DBLayer{..} = fmap Resp . go mapExceptT atomically $ isStakeKeyRegistered (PrimaryKey wid) PutTxHistory wid txs -> catchNoSuchWallet Unit $ mapExceptT atomically $ putTxHistory (PrimaryKey wid) txs - ReadTxHistory wid order range status -> Right . TxHistory <$> - atomically (readTxHistory (PrimaryKey wid) order range status) + ReadTxHistory wid minWith order range status -> Right . TxHistory <$> + atomically (readTxHistory (PrimaryKey wid) minWith order range status) RemovePendingTx wid tid -> catchCannotRemovePendingTx Unit $ mapExceptT atomically $ removePendingTx (PrimaryKey wid) tid PutPrivateKey wid pk -> catchNoSuchWallet Unit $ @@ -583,7 +589,12 @@ generator (Model _ wids) = Just $ frequency $ fmap (fmap At) <$> concat , (5, PutDelegationCertificate <$> genId' <*> arbitrary <*> arbitrary) , (1, IsStakeKeyRegistered <$> genId') , (5, PutTxHistory <$> genId' <*> fmap unGenTxHistory arbitrary) - , (5, ReadTxHistory <$> genId' <*> genSortOrder <*> genRange <*> arbitrary) + , (5, ReadTxHistory + <$> genId' + <*> genMinWithdrawal + <*> genSortOrder + <*> genRange + <*> arbitrary) , (4, RemovePendingTx <$> genId' <*> arbitrary) , (3, PutPrivateKey <$> genId' <*> genPrivKey) , (3, ReadPrivateKey <$> genId') @@ -605,6 +616,12 @@ generator (Model _ wids) = Just $ frequency $ fmap (fmap At) <$> concat genRange :: Gen (Range SlotId) genRange = applyArbitrary2 Range + genMinWithdrawal :: Gen (Maybe (Quantity "lovelace" Natural)) + genMinWithdrawal = frequency + [ (10, pure Nothing) + , (1, (Just . Quantity . fromIntegral @Word64) <$> arbitrary ) + ] + isUnordered :: Ord x => [x] -> Bool isUnordered xs = xs /= L.sort xs @@ -632,8 +649,8 @@ shrinker (Model _ _) (At cmd) = case cmd of [ At $ RollbackTo wid sid' | sid' <- shrink sid ] - ReadTxHistory wid so range status -> - [ At $ ReadTxHistory wid so range' status + ReadTxHistory wid minW so range status -> + [ At $ ReadTxHistory wid minW so range' status | range' <- shrink range ] _ -> [] @@ -950,7 +967,7 @@ tag = Foldl.fold $ catMaybes <$> sequenceA isReadTxHistory :: Event s Symbolic -> Maybe MWid isReadTxHistory ev = case (cmd ev, mockResp ev, before ev) of - (At (ReadTxHistory wid _ _ _), Resp (Right (TxHistory _)), Model _ wids) + (At (ReadTxHistory wid _ _ _ _), Resp (Right (TxHistory _)), Model _ wids) -> Just (wids ! wid) _otherwise -> Nothing diff --git a/lib/core/test/unit/Cardano/WalletSpec.hs b/lib/core/test/unit/Cardano/WalletSpec.hs index e4a1849baf4..25da31c3320 100644 --- a/lib/core/test/unit/Cardano/WalletSpec.hs +++ b/lib/core/test/unit/Cardano/WalletSpec.hs @@ -503,7 +503,7 @@ walletListTransactionsSorted wallet@(wid, _, _) _order (_mstart, _mend) history (WalletLayerFixture DBLayer{..} wl _ slotIdTime) <- liftIO $ setupFixture wallet atomically $ unsafeRunExceptT $ putTxHistory (PrimaryKey wid) history txs <- unsafeRunExceptT $ - W.listTransactions wl wid Nothing Nothing Descending + W.listTransactions wl wid Nothing Nothing Nothing Descending length txs `shouldBe` L.length history -- With the 'Down'-wrapper, the sort is descending. txs `shouldBe` L.sortOn (Down . slotId . txInfoMeta) txs diff --git a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Api/Server.hs b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Api/Server.hs index fc129f12ba9..9c5e2278927 100644 --- a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Api/Server.hs +++ b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Api/Server.hs @@ -240,8 +240,8 @@ server byron icarus jormungandr spl ntp = (\_ _ -> throwError err501) :<|> (\wid r0 r1 s -> withLegacyLayer wid - (byron , listTransactions byron wid r0 r1 s) - (icarus, listTransactions icarus wid r0 r1 s) + (byron , listTransactions byron wid Nothing r0 r1 s) + (icarus, listTransactions icarus wid Nothing r0 r1 s) ) :<|> (\_ _ -> throwError err501) diff --git a/lib/jormungandr/test/integration/Main.hs b/lib/jormungandr/test/integration/Main.hs index 8af61f6d0b1..3773ab863c1 100644 --- a/lib/jormungandr/test/integration/Main.hs +++ b/lib/jormungandr/test/integration/Main.hs @@ -30,7 +30,7 @@ import Cardano.Startup import Cardano.Wallet.Api.Server ( Listen (..) ) import Cardano.Wallet.Api.Types - ( DecodeAddress (..), EncodeAddress (..) ) + ( DecodeAddress (..), EncodeAddress (..), EncodeStakeAddress (..) ) import Cardano.Wallet.Jormungandr ( serveWallet, setupTracers, tracerSeverities ) import Cardano.Wallet.Jormungandr.Compatibility @@ -171,6 +171,7 @@ specWithServer ( NetworkDiscriminantVal n , DecodeAddress n , EncodeAddress n + , EncodeStakeAddress n , DelegationAddress n JormungandrKey ) => Trace IO Text diff --git a/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/Transactions.hs b/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/Transactions.hs index 466d916e9fc..99026697525 100644 --- a/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/Transactions.hs +++ b/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/Transactions.hs @@ -28,6 +28,7 @@ import Cardano.Wallet.Api.Types , ApiTxId (..) , ApiWallet , DecodeAddress + , DecodeStakeAddress , EncodeAddress , WalletStyle (..) ) @@ -123,6 +124,7 @@ import qualified Network.HTTP.Types.Status as HTTP spec :: forall n t. ( KnownNetwork n , DecodeAddress n + , DecodeStakeAddress n , EncodeAddress n , DelegationAddress n JormungandrKey ) => SpecWith (Context t) @@ -447,6 +449,7 @@ data ExternalTxFixture = ExternalTxFixture fixtureExternalTx :: forall n t. ( DecodeAddress n + , DecodeStakeAddress n , DelegationAddress n JormungandrKey ) => (Context t) diff --git a/lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs b/lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs index f3563579fea..352a68d50a8 100644 --- a/lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs +++ b/lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs @@ -271,8 +271,8 @@ server byron icarus shelley spl ntp = ) :<|> (\wid r0 r1 s -> withLegacyLayer wid - (byron , listTransactions byron wid r0 r1 s) - (icarus, listTransactions icarus wid r0 r1 s) + (byron , listTransactions byron wid Nothing r0 r1 s) + (icarus, listTransactions icarus wid Nothing r0 r1 s) ) :<|> (\wid tx -> withLegacyLayer wid diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index 430377dbac0..b66f0775779 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -1403,8 +1403,8 @@ x-parametersMinWithdrawal: ¶metersMinWithdrawal in: query name: minWithdrawal description: | - Returns only transactions that have withdrawals above the given amount. This is particularly useful to list - all transactions with non-empty withdrawals. + Returns only transactions that have at least one withdrawal above the given amount. + This is particularly useful when set to `1` in order to list the withdrawal history of a wallet. schema: type: integer minimum: 0