diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 4288a3b4d0..eb2e555e0b 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -36,7 +36,7 @@ use bigdecimal::BigDecimal; use common::executor::{spawn, Timer}; use common::mm_ctx::{from_ctx, MmArc}; use common::mm_metrics::MetricsWeak; -use common::{block_on, rpc_err_response, rpc_response, HyRes}; +use common::{block_on, calc_total_pages, rpc_err_response, rpc_response, HyRes}; use futures::compat::Future01CompatExt; use futures::lock::Mutex as AsyncMutex; use futures01::Future; @@ -46,6 +46,7 @@ use rpc::v1::types::Bytes as BytesJson; use serde_json::{self as json, Value as Json}; use std::collections::hash_map::{HashMap, RawEntryMut}; use std::fmt; +use std::num::NonZeroUsize; use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; @@ -886,6 +887,7 @@ struct MyTxHistoryRequest { max: bool, #[serde(default = "ten")] limit: usize, + page_number: Option, } /// Returns the transaction history of selected coin. Returns no more than `limit` records (default: 10). @@ -922,7 +924,10 @@ pub fn my_tx_history(ctx: MmArc, req: Json) -> HyRes { .ok_or(format!("from_id {:02x} is not found", id))) + 1 }, - None => 0, + None => match request.page_number { + Some(page_n) => (page_n.get() - 1) * request.limit, + None => 0, + }, }; let history = history.into_iter().skip(skip).take(limit); let history: Vec = history @@ -950,6 +955,8 @@ pub fn my_tx_history(ctx: MmArc, req: Json) -> HyRes { "total": total_records, "current_block": block_number, "sync_status": coin.history_sync_status(), + "page_number": request.page_number, + "total_pages": calc_total_pages(total_records, request.limit), } }) .to_string(), diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index 1cb5312907..45fefb5916 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -2127,3 +2127,25 @@ fn test_median() { let actual = median(&mut input); assert_eq!(expected, actual); } + +pub fn calc_total_pages(entries_len: usize, limit: usize) -> usize { + if limit == 0 { + return 0; + } + let pages_num = entries_len / limit; + if entries_len % limit == 0 { + pages_num + } else { + pages_num + 1 + } +} + +#[test] +fn test_calc_total_pages() { + assert_eq!(0, calc_total_pages(0, 0)); + assert_eq!(0, calc_total_pages(0, 1)); + assert_eq!(0, calc_total_pages(0, 100)); + assert_eq!(1, calc_total_pages(1, 1)); + assert_eq!(2, calc_total_pages(16, 8)); + assert_eq!(2, calc_total_pages(15, 8)); +} diff --git a/mm2src/lp_swap.rs b/mm2src/lp_swap.rs index 7b221d6d85..653cd87823 100644 --- a/mm2src/lp_swap.rs +++ b/mm2src/lp_swap.rs @@ -60,7 +60,7 @@ use crate::mm2::lp_network::broadcast_p2p_msg; use async_std::sync as async_std_sync; use bigdecimal::BigDecimal; use coins::{lp_coinfind, TradeFee, TransactionEnum}; -use common::{bits256, block_on, +use common::{bits256, block_on, calc_total_pages, executor::{spawn, Timer}, mm_ctx::{from_ctx, MmArc}, mm_number::MmNumber, @@ -73,6 +73,7 @@ use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; use serde_json::{self as json, Value as Json}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; +use std::num::NonZeroUsize; use std::path::PathBuf; use std::str::FromStr; use std::sync::{Arc, Mutex, Weak}; @@ -449,6 +450,18 @@ pub fn active_swaps_using_coin(ctx: &MmArc, coin: &str) -> Result, Str Ok(uuids) } +pub fn active_swaps(ctx: &MmArc) -> Result, String> { + let swap_ctx = try_s!(SwapsContext::from_ctx(&ctx)); + let swaps = try_s!(swap_ctx.running_swaps.lock()); + let mut uuids = vec![]; + for swap in swaps.iter() { + if let Some(swap) = swap.upgrade() { + uuids.push(*swap.uuid()) + } + } + Ok(uuids) +} + #[derive(Clone, Copy, Debug)] pub struct SwapConfirmationsSettings { pub maker_coin_confs: u64, @@ -810,13 +823,14 @@ pub fn save_stats_swap_status(ctx: &MmArc, data: Json) { unwrap!(save_stats_swap(ctx, &swap)); } -fn ten() -> u64 { 10 } +fn ten() -> usize { 10 } #[derive(Debug, Deserialize)] struct MyRecentSwapsReq { #[serde(default = "ten")] - limit: u64, + limit: usize, from_uuid: Option, + page_number: Option, } /// Returns the data of recent swaps of `my` node. Returns no more than `limit` records (default: 10). @@ -836,14 +850,17 @@ pub fn my_recent_swaps(ctx: MmArc, req: Json) -> HyRes { .ok_or(format!("from_uuid {} swap is not found", uuid))) + 1 }, - None => 0, + None => match req.page_number { + Some(page_n) => (page_n.get() - 1) * req.limit, + None => 0, + }, }; // iterate over file entries trying to parse the file contents and add to result vector let swaps: Vec = entries .iter() .skip(skip) - .take(req.limit as usize) + .take(req.limit) .map( |(_, path)| match json::from_slice::(&unwrap!(slurp(&path))) { Ok(swap) => unwrap!(json::to_value(MySwapStatusResponse::from(&swap))), @@ -864,6 +881,8 @@ pub fn my_recent_swaps(ctx: MmArc, req: Json) -> HyRes { "skipped": skip, "limit": req.limit, "total": entries.len(), + "page_number": req.page_number, + "total_pages": calc_total_pages(entries.len(), req.limit), }, }) .to_string(), @@ -1055,6 +1074,53 @@ pub async fn unban_pubkeys(ctx: MmArc, req: Json) -> Result>, S Ok(try_s!(Response::builder().body(res))) } +#[derive(Deserialize)] +struct ActiveSwapsReq { + #[serde(default)] + include_status: bool, +} + +#[derive(Serialize)] +struct ActiveSwapsRes { + uuids: Vec, + statuses: Option>, +} + +pub async fn active_swaps_rpc(ctx: MmArc, req: Json) -> Result>, String> { + let req: ActiveSwapsReq = try_s!(json::from_value(req)); + let uuids = try_s!(active_swaps(&ctx)); + let statuses = if req.include_status { + let mut map = HashMap::new(); + for uuid in uuids.iter() { + let path = my_swap_file_path(&ctx, &uuid); + let content = match slurp(&path) { + Ok(c) => c, + Err(e) => { + common::log::error!("Error {} on slurp({})", e, path.display()); + continue; + }, + }; + if content.is_empty() { + continue; + } + let status: SavedSwap = match json::from_slice(&content) { + Ok(s) => s, + Err(e) => { + common::log::error!("Error {} on deserializing the content {:?}", e, content); + continue; + }, + }; + map.insert(*uuid, status); + } + Some(map) + } else { + None + }; + let result = ActiveSwapsRes { uuids, statuses }; + let res = try_s!(json::to_vec(&result)); + Ok(try_s!(Response::builder().body(res))) +} + #[cfg(test)] mod lp_swap_tests { use super::*; diff --git a/mm2src/rpc.rs b/mm2src/rpc.rs index 3d38133843..3f30602973 100644 --- a/mm2src/rpc.rs +++ b/mm2src/rpc.rs @@ -37,8 +37,9 @@ use std::net::SocketAddr; use crate::mm2::lp_ordermatch::{buy, cancel_all_orders, cancel_order, my_orders, order_status, orderbook, sell, set_price}; -use crate::mm2::lp_swap::{coins_needed_for_kick_start, import_swaps, list_banned_pubkeys, max_taker_vol, - my_recent_swaps, my_swap_status, recover_funds_of_swap, stats_swap_status, unban_pubkeys}; +use crate::mm2::lp_swap::{active_swaps_rpc, coins_needed_for_kick_start, import_swaps, list_banned_pubkeys, + max_taker_vol, my_recent_swaps, my_swap_status, recover_funds_of_swap, stats_swap_status, + unban_pubkeys}; #[path = "rpc/lp_commands.rs"] pub mod lp_commands; use self::lp_commands::*; @@ -117,6 +118,7 @@ pub fn dispatcher(req: Json, ctx: MmArc) -> DispatcherRes { DispatcherRes::Match(match &method[..] { // Sorted alphanumerically (on the first latter) for readability. // "autoprice" => lp_autoprice (ctx, req), + "active_swaps" => hyres(active_swaps_rpc(ctx, req)), "buy" => hyres(buy(ctx, req)), "cancel_all_orders" => hyres(cancel_all_orders(ctx, req)), "cancel_order" => hyres(cancel_order(ctx, req)),