From 404ceb39b9fb879ac4f46f1b20df0eeef1ae79cc Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Dec 2022 16:56:37 +0100 Subject: [PATCH] Option to limit amount of transfers. --- src/bin/cli.rs | 10 +++++++++- src/graph/flow.rs | 39 ++++++++++++++++++++++++++++++++++----- src/server.rs | 2 ++ tests/integration.rs | 2 +- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/bin/cli.rs b/src/bin/cli.rs index 757341e..3d0940b 100644 --- a/src/bin/cli.rs +++ b/src/bin/cli.rs @@ -41,7 +41,7 @@ fn main() { return; } - if args.len() < 4 { + if args.len() < 5 { println!("Usage: cli [--csv] [--safes] [--dot ]"); println!( "Usage: cli [--csv] [--safes] [--dot ]" @@ -49,12 +49,16 @@ fn main() { println!( "Usage: cli [--csv] [--safes] [--dot ]" ); + println!( + "Usage: cli [--csv] [--safes] [--dot ]" + ); println!("Option --csv reads edges.dat in csv format instead of binary."); println!("Option --safes reads a safes.dat file instead of an edges.dat file."); return; } let mut max_hops = None; let mut max_flow = U256::MAX; + let mut max_transfers: Option = None; let (from_str, to_str, edges_file) = (&args[1], &args[2], &args[3]); if args.len() >= 5 { max_hops = Some( @@ -64,6 +68,9 @@ fn main() { ); if args.len() >= 6 { max_flow = args[5].as_str().into(); + if args.len() >= 7 { + max_transfers = Some(args[6].as_str().parse::().unwrap() as u64); + } } } @@ -83,6 +90,7 @@ fn main() { &edges, max_flow, max_hops, + max_transfers, ); println!("Found flow: {}", flow.to_decimal()); //println!("{:?}", transfers); diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 1b3645a..c0b861d 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -13,6 +13,7 @@ pub fn compute_flow( edges: &EdgeDB, requested_flow: U256, max_distance: Option, + max_transfers: Option, ) -> (U256, Vec) { let mut adjacencies = Adjacencies::new(edges); let mut used_edges: HashMap> = HashMap::new(); @@ -59,6 +60,15 @@ pub fn compute_flow( flow = requested_flow + still_to_prune; } + if let Some(max_transfers) = max_transfers { + let lost = reduce_transfers(max_transfers * 3, &mut used_edges); + println!( + "Capacity lost by transfer count reduction: {}", + lost.to_decimal_fraction() + ); + flow -= lost; + } + let transfers = if flow == U256::from(0) { vec![] } else { @@ -219,6 +229,25 @@ fn prune_flow( flow_to_prune } +fn reduce_transfers( + max_transfers: u64, + used_edges: &mut HashMap>, +) -> U256 { + let mut reduced_flow = U256::from(0); + while used_edges.len() > max_transfers as usize { + let all_edges = used_edges + .iter() + .flat_map(|(f, e)| e.iter().map(|(t, c)| ((f.clone(), t.clone()), c))); + if all_edges.clone().count() <= max_transfers as usize { + return reduced_flow; + } + let ((f, t), c) = all_edges.min_by_key(|(_, c)| *c).unwrap(); + reduced_flow += *c; + prune_edge(used_edges, (&f, &t), *c); + } + reduced_flow +} + /// Returns a map from the negative shortest path length to the edge. /// The shortest path length is negative so that it is sorted by /// longest paths first - those are the ones we want to eliminate first. @@ -531,7 +560,7 @@ mod test { token: t, capacity: U256::from(10), }]); - let flow = compute_flow(&a, &b, &edges, U256::MAX, None); + let flow = compute_flow(&a, &b, &edges, U256::MAX, None, None); assert_eq!( flow, ( @@ -563,7 +592,7 @@ mod test { capacity: U256::from(8), }, ]); - let flow = compute_flow(&a, &c, &edges, U256::MAX, None); + let flow = compute_flow(&a, &c, &edges, U256::MAX, None, None); assert_eq!( flow, ( @@ -615,7 +644,7 @@ mod test { capacity: U256::from(8), }, ]); - let mut flow = compute_flow(&a, &d, &edges, U256::MAX, None); + let mut flow = compute_flow(&a, &d, &edges, U256::MAX, None, None); flow.1.sort(); assert_eq!( flow, @@ -649,7 +678,7 @@ mod test { ] ) ); - let mut pruned_flow = compute_flow(&a, &d, &edges, U256::from(6), None); + let mut pruned_flow = compute_flow(&a, &d, &edges, U256::from(6), None, None); pruned_flow.1.sort(); assert_eq!( pruned_flow, @@ -707,7 +736,7 @@ mod test { capacity: U256::from(8), }, ]); - let mut flow = compute_flow(&a, &d, &edges, U256::MAX, None); + let mut flow = compute_flow(&a, &d, &edges, U256::MAX, None, None); flow.1.sort(); println!("{:?}", &flow.1); assert_eq!(flow.0, U256::from(9)); diff --git a/src/server.rs b/src/server.rs index 767f89c..5f08574 100644 --- a/src/server.rs +++ b/src/server.rs @@ -143,6 +143,7 @@ fn compute_transfer( } else { vec![None] }; + let max_transfers = request.params["max_transfers"].as_u64(); for max_distance in max_distances { let (flow, transfers) = graph::compute_flow( &Address::from(request.params["from"].to_string().as_str()), @@ -154,6 +155,7 @@ fn compute_transfer( U256::MAX }, max_distance, + max_transfers, ); println!("Computed flow with max distance {max_distance:?}: {flow}"); socket.write_all( diff --git a/tests/integration.rs b/tests/integration.rs index 79af0fb..36d223e 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -61,7 +61,7 @@ fn test_flow( requested_flow: U256, max_distance: Option, ) { - let transfers = compute_flow(source, sink, edges, requested_flow, max_distance); + let transfers = compute_flow(source, sink, edges, requested_flow, max_distance, None); println!("{transfers:?}"); let token_owners = transfers