Create CLI.
This commit is contained in:
parent
9ff1cc0c85
commit
8a53afc8f8
9 changed files with 160 additions and 32 deletions
|
@ -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
82
src/bin/cli.rs
Normal 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
12
src/bin/server.rs
Normal 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);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -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,
|
|
||||||
// );
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue