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"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
default-run = "server"
|
||||
|
||||
# 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)
|
||||
}
|
||||
|
||||
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(
|
||||
source: &Address,
|
||||
sink: &Address,
|
||||
|
|
|
@ -36,3 +36,4 @@ impl Display for Node {
|
|||
}
|
||||
|
||||
pub use crate::graph::flow::compute_flow;
|
||||
pub use crate::graph::flow::transfers_to_dot;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod graph;
|
||||
pub mod io;
|
||||
pub mod server;
|
||||
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)]
|
||||
pub struct Address([u8; 20]);
|
||||
|
||||
impl Address {
|
||||
pub fn short(&self) -> String {
|
||||
format!("{self}")[..8].to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Address {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use std::fmt::Debug;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
||||
|
||||
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]);
|
||||
|
||||
impl U256 {
|
||||
|
@ -12,11 +13,27 @@ impl U256 {
|
|||
U256([high, low])
|
||||
}
|
||||
pub const MAX: U256 = U256::new(u128::MAX, u128::MAX);
|
||||
#[allow(dead_code)]
|
||||
pub fn to_decimal(self) -> String {
|
||||
let value = BigUint::from(self.0[0]) << 128 | BigUint::from(self.0[1]);
|
||||
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 {
|
||||
|
@ -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)]
|
||||
mod test {
|
||||
use super::U256;
|
||||
|
|
Loading…
Reference in a new issue