Create CLI.

This commit is contained in:
chriseth 2022-10-08 13:32:36 +02:00
parent 9ff1cc0c85
commit 8a53afc8f8
9 changed files with 160 additions and 32 deletions

View file

@ -2,6 +2,7 @@
name = "pathfinder2" name = "pathfinder2"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
default-run = "server"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

82
src/bin/cli.rs Normal file
View file

@ -0,0 +1,82 @@
use std::env;
use std::fs::File;
use std::io::Write;
use pathfinder2::graph;
use pathfinder2::io;
use pathfinder2::types::Address;
use pathfinder2::types::U256;
const HUB_ADDRESS: &str = "0x29b9a7fBb8995b2423a71cC17cf9810798F6C543";
const TRANSFER_THROUGH_SIG: &str = "transferThrough(address[],address[],address[],uint256[])";
const RPC_URL: &str = "https://rpc.gnosischain.com";
fn main() {
let (dotfile, args) = if env::args().len() >= 2 && env::args().nth_back(1).unwrap() == "--dot" {
(
Some(env::args().last().unwrap()),
env::args().rev().skip(2).rev().collect::<Vec<_>>(),
)
} else {
(None, env::args().collect::<Vec<_>>())
};
if args.len() < 4 {
println!("Usage: cli <from> <to> <edges.dat> [--dot <dotfile>]");
println!("Usage: cli <from> <to> <max_hops> <edges.dat> [--dot <dotfile>]");
println!("Usage: cli <from> <to> <max_hops> <max_flow> <edges.dat> [--dot <dotfile>]");
return;
}
let mut max_hops = None;
let mut max_flow = U256::MAX;
let (from_str, to_str, edges_file) = (&args[1], &args[2], &args[3]);
if args.len() >= 5 {
max_hops = Some(args[4].parse().unwrap());
if args.len() >= 6 {
max_flow = args[5].as_str().into();
}
}
println!("Computing flow {from_str} -> {to_str} using {edges_file}");
let edges = io::read_edges_binary(edges_file).expect("Error loading edges.");
println!("Read {} edges", edges.len());
let (flow, transfers) = graph::compute_flow(
&Address::from(from_str.as_str()),
&Address::from(to_str.as_str()),
&edges,
max_flow,
max_hops,
);
println!("Max flow: {flow}");
println!("{:?}", transfers);
let token_owners = transfers
.iter()
.map(|e| e.token.to_string())
.collect::<Vec<String>>()
.join(",");
let froms = transfers
.iter()
.map(|e| e.from.to_string())
.collect::<Vec<String>>()
.join(",");
let tos = transfers
.iter()
.map(|e| e.to.to_string())
.collect::<Vec<String>>()
.join(",");
let amounts = transfers
.iter()
.map(|e| e.capacity.to_decimal())
.collect::<Vec<String>>()
.join(",");
println!("To check, run the following command (requires foundry):");
println!("cast call '{HUB_ADDRESS}' '{TRANSFER_THROUGH_SIG}' '[{token_owners}]' '[{froms}]' '[{tos}]' '[{amounts}]' --rpc-url {RPC_URL} --from {}", &transfers[0].from.to_string());
if let Some(dotfile) = dotfile {
File::create(&dotfile)
.unwrap()
.write_all(graph::transfers_to_dot(&transfers).as_bytes())
.unwrap();
println!("Wrote dotfile {dotfile}.");
}
}

12
src/bin/server.rs Normal file
View file

@ -0,0 +1,12 @@
use std::env;
use pathfinder2::server;
fn main() {
let port = if env::args().len() == 1 {
8080
} else {
env::args().nth(1).unwrap().as_str().parse::<u16>().unwrap()
};
server::start_server(port, 10, 4);
}

View file

@ -65,6 +65,38 @@ pub fn compute_flow(
(flow, transfers) (flow, transfers)
} }
pub fn transfers_to_dot(edges: &Vec<Edge>) -> String {
let mut out = String::new();
writeln!(out, "digraph transfers {{").expect("");
for Edge {
from,
to,
token,
capacity,
} in edges
{
let t = if token == from {
"(trust)".to_string()
} else if token == to {
String::new()
} else {
format!(" ({})", token.short())
};
writeln!(
out,
" \"{}\" -> \"{}\" [label=\"{}{}\"];",
from.short(),
to.short(),
capacity.to_decimal_fraction(),
t
)
.expect("");
}
writeln!(out, "}}").expect("");
out
}
fn augmenting_path( fn augmenting_path(
source: &Address, source: &Address,
sink: &Address, sink: &Address,

View file

@ -36,3 +36,4 @@ impl Display for Node {
} }
pub use crate::graph::flow::compute_flow; pub use crate::graph::flow::compute_flow;
pub use crate::graph::flow::transfers_to_dot;

View file

@ -1,3 +1,4 @@
pub mod graph; pub mod graph;
pub mod io; pub mod io;
pub mod server;
pub mod types; pub mod types;

View file

@ -1,30 +0,0 @@
use std::env;
mod graph;
mod io;
mod server;
mod types;
fn main() {
let port = if env::args().len() == 1 {
8080
} else {
env::args().nth(1).unwrap().as_str().parse::<u16>().unwrap()
};
server::start_server(port, 10, 4);
// let args: Vec<String> = env::args().collect();
// if args.len() != 4 {
// panic!("Expected three arguments");
// }
// let (from_str, to_str, edges_file) = (&args[1], &args[2], &args[3]);
// println!("Computing flow {from_str} -> {to_str} using {edges_file}");
// let edges = io::read_edges_binary(edges_file).expect("Error loading edges.");
// println!("Read {} edges", edges.len());
// flow::compute_flow(
// &Address::from(from_str.as_str()),
// &Address::from(to_str.as_str()),
// &edges,
// );
}

View file

@ -3,6 +3,12 @@ use std::fmt::{Debug, Display, Formatter};
#[derive(Clone, Copy, Default, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(Clone, Copy, Default, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Address([u8; 20]); pub struct Address([u8; 20]);
impl Address {
pub fn short(&self) -> String {
format!("{self}")[..8].to_string()
}
}
impl Debug for Address { impl Debug for Address {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self) write!(f, "{}", self)

View file

@ -1,10 +1,11 @@
use std::fmt::Debug;
use std::fmt::Display; use std::fmt::Display;
use std::fmt::Formatter; use std::fmt::Formatter;
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign}; use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
use num_bigint::BigUint; use num_bigint::BigUint;
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct U256([u128; 2]); pub struct U256([u128; 2]);
impl U256 { impl U256 {
@ -12,11 +13,27 @@ impl U256 {
U256([high, low]) U256([high, low])
} }
pub const MAX: U256 = U256::new(u128::MAX, u128::MAX); pub const MAX: U256 = U256::new(u128::MAX, u128::MAX);
#[allow(dead_code)]
pub fn to_decimal(self) -> String { pub fn to_decimal(self) -> String {
let value = BigUint::from(self.0[0]) << 128 | BigUint::from(self.0[1]); let value = BigUint::from(self.0[0]) << 128 | BigUint::from(self.0[1]);
format!("{}", value) format!("{}", value)
} }
pub fn to_decimal_fraction(self) -> String {
let value = BigUint::from(self.0[0]) << 128 | BigUint::from(self.0[1]);
let formatted = format!("{}", value);
match formatted.len() {
18.. => {
format!(
"{}.{}",
&formatted[..formatted.len() - 18],
&formatted[formatted.len() - 18..formatted.len() - 16]
)
}
17 => {
format!(".0{}", &formatted[0..1],)
}
_ => "0+eps".to_string(),
}
}
} }
impl From<u128> for U256 { impl From<u128> for U256 {
@ -116,6 +133,12 @@ impl Display for U256 {
} }
} }
impl Debug for U256 {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::U256; use super::U256;