pathfinder2/src/io.rs
2022-12-20 20:44:32 +01:00

266 lines
7.6 KiB
Rust

use std::collections::{BTreeMap, BTreeSet};
use std::fs::File;
use std::io::{self, BufRead};
use std::io::{Read, Write};
use std::{collections::HashMap, io::BufReader};
use crate::safe_db::db::DB;
use crate::types::edge::EdgeDB;
use crate::types::{Address, Edge, Safe, U256};
pub fn read_edges_binary(path: &String) -> Result<EdgeDB, io::Error> {
let mut f = File::open(path)?;
let address_index = read_address_index(&mut f)?;
read_edges(&mut f, &address_index)
}
pub fn read_edges_csv(path: &String) -> Result<EdgeDB, io::Error> {
let mut edges = Vec::new();
let f = BufReader::new(File::open(path)?);
for line in f.lines() {
let line = line?;
match &line.split(',').collect::<Vec<_>>()[..] {
[] => continue,
[from, to, token, capacity] => {
let from = Address::from(unescape(from));
let to = Address::from(unescape(to));
let token = Address::from(unescape(token));
let capacity = U256::from(unescape(capacity));
edges.push(Edge {
from,
to,
token,
capacity,
});
}
_ => {
return Result::Err(io::Error::new(
io::ErrorKind::Other,
format!("Expected from,to,token,capacity, but got {line}"),
))
}
}
}
Ok(EdgeDB::new(edges))
}
pub fn write_edges_binary(edges: &EdgeDB, path: &String) -> Result<(), io::Error> {
let mut file = File::create(path)?;
let address_index = write_address_index(&mut file, edges)?;
write_edges(&mut file, edges, &address_index)
}
pub fn write_edges_csv(edges: &EdgeDB, path: &String) -> Result<(), io::Error> {
let mut file = File::create(path)?;
let mut sorted_edges = edges.edges().clone();
sorted_edges.sort();
for Edge {
from,
to,
token,
capacity,
} in sorted_edges
{
writeln!(file, "{from},{to},{token},{capacity}")?;
}
Ok(())
}
pub fn import_from_safes_binary(path: &str) -> Result<DB, io::Error> {
let mut f = File::open(path)?;
let mut safes: BTreeMap<Address, Safe> = Default::default();
let address_index = read_address_index(&mut f)?;
// organizations
for _ in 0..read_u32(&mut f)? {
let org_address = read_address(&mut f, &address_index)?;
safes.entry(org_address).or_default().organization = true;
}
// trust edges
for _ in 0..read_u32(&mut f)? {
let user = read_address(&mut f, &address_index)?;
assert!(user != Address::default());
let send_to = read_address(&mut f, &address_index)?;
assert!(send_to != Address::default());
let limit_percentage = read_u8(&mut f)?;
assert!(limit_percentage <= 100);
if send_to != user && limit_percentage > 0 {
safes
.entry(user)
.or_default()
.limit_percentage
.insert(send_to, limit_percentage);
}
}
// balances
for _ in 0..read_u32(&mut f)? {
let user = read_address(&mut f, &address_index)?;
assert!(user != Address::default());
let token_owner = read_address(&mut f, &address_index)?;
assert!(token_owner != Address::default());
let balance = read_u256(&mut f)?;
if balance != U256::from(0) {
safes
.entry(user)
.or_default()
.balances
.insert(token_owner, balance);
}
}
// we use the safe address as token address
let mut token_owner = BTreeMap::default();
for (addr, safe) in &mut safes {
safe.token_address = *addr;
token_owner.insert(*addr, *addr);
}
Ok(DB::new(safes, token_owner))
}
fn read_address_index(file: &mut File) -> Result<HashMap<u32, Address>, io::Error> {
let address_count = read_u32(file)?;
let mut addresses = HashMap::new();
for i in 0..address_count {
let mut buf = [0; 20];
file.read_exact(&mut buf)?;
addresses.insert(i, Address::from(buf));
}
Ok(addresses)
}
fn write_address_index(
file: &mut File,
edges: &EdgeDB,
) -> Result<HashMap<Address, u32>, io::Error> {
let mut addresses = BTreeSet::new();
for Edge {
from, to, token, ..
} in edges.edges()
{
addresses.insert(*from);
addresses.insert(*to);
addresses.insert(*token);
}
write_u32(file, addresses.len() as u32)?;
let mut index = HashMap::new();
for (i, addr) in addresses.into_iter().enumerate() {
file.write_all(&addr.to_bytes())?;
index.insert(addr, i as u32);
}
Ok(index)
}
fn read_u32(file: &mut File) -> Result<u32, io::Error> {
let mut buf = [0; 4];
file.read_exact(&mut buf)?;
Ok(u32::from_be_bytes(buf))
}
fn write_u32(file: &mut File, v: u32) -> Result<(), io::Error> {
let buf = v.to_be_bytes();
file.write_all(&buf)
}
fn read_u8(file: &mut File) -> Result<u8, io::Error> {
let mut buf = [0; 1];
file.read_exact(&mut buf)?;
Ok(u8::from_be_bytes(buf))
}
fn write_u8(file: &mut File, v: u8) -> Result<(), io::Error> {
let buf = v.to_be_bytes();
file.write_all(&buf)
}
fn read_address(
file: &mut File,
address_index: &HashMap<u32, Address>,
) -> Result<Address, io::Error> {
let index = read_u32(file)?;
Ok(address_index[&index])
}
fn write_address(
file: &mut File,
address: &Address,
address_index: &HashMap<Address, u32>,
) -> Result<(), io::Error> {
write_u32(file, *address_index.get(address).unwrap())
}
fn read_u256(file: &mut File) -> Result<U256, io::Error> {
let length = read_u8(file)? as usize;
let mut bytes = [0u8; 32];
file.read_exact(&mut bytes[32 - length..32])?;
let high = u128::from_be_bytes(*<&[u8; 16]>::try_from(&bytes[0..16]).unwrap());
let low = u128::from_be_bytes(*<&[u8; 16]>::try_from(&bytes[16..32]).unwrap());
Ok(U256::new(high, low))
}
fn write_u256(file: &mut File, v: &U256) -> Result<(), io::Error> {
let v_bytes = v.to_bytes();
if v_bytes.is_empty() {
file.write_all(&[1, 0])
} else {
write_u8(file, v_bytes.len() as u8)?;
file.write_all(&v_bytes)
}
}
fn read_edges(file: &mut File, address_index: &HashMap<u32, Address>) -> Result<EdgeDB, io::Error> {
let edge_count = read_u32(file)?;
let mut edges = Vec::new();
for _i in 0..edge_count {
let from = read_address(file, address_index)?;
let to = read_address(file, address_index)?;
let token = read_address(file, address_index)?;
let capacity = read_u256(file)?;
edges.push(Edge {
from,
to,
token,
capacity,
});
}
Ok(EdgeDB::new(edges))
}
fn write_edges(
file: &mut File,
edges: &EdgeDB,
address_index: &HashMap<Address, u32>,
) -> Result<(), io::Error> {
write_u32(file, edges.edge_count() as u32)?;
let mut sorted_edges = edges.edges().clone();
sorted_edges.sort();
for Edge {
from,
to,
token,
capacity,
} in &sorted_edges
{
write_address(file, from, address_index)?;
write_address(file, to, address_index)?;
write_address(file, token, address_index)?;
write_u256(file, capacity)?;
}
Ok(())
}
fn unescape(input: &str) -> &str {
match input.chars().next() {
Some('"') | Some('\'') => {
assert!(input.len() >= 2 && input.chars().last() == input.chars().next());
&input[1..input.len() - 1]
}
_ => input,
}
}