From f37b35d6ec36b072a287101518335e74b529ae90 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Dec 2022 21:23:38 +0100 Subject: [PATCH] Fix trust limit problem. --- src/graph/adjacencies.rs | 61 ++++++++++++++++++++++++++++------------ src/graph/flow.rs | 61 +++++++++++++++++++++++++++++++++------- src/graph/mod.rs | 26 +++++++++++++---- src/types/edge.rs | 11 ++++++++ 4 files changed, 126 insertions(+), 33 deletions(-) diff --git a/src/graph/adjacencies.rs b/src/graph/adjacencies.rs index 13fc32c..b4da10a 100644 --- a/src/graph/adjacencies.rs +++ b/src/graph/adjacencies.rs @@ -1,7 +1,7 @@ use crate::graph::Node; use crate::types::edge::EdgeDB; -use crate::types::{Address, Edge, U256}; -use std::cmp::Reverse; +use crate::types::{Edge, U256}; +use std::cmp::{max, Reverse}; use std::collections::HashMap; pub struct Adjacencies<'a> { @@ -10,17 +10,25 @@ pub struct Adjacencies<'a> { capacity_adjustments: HashMap>, } -fn pseudo_node(edge: Edge) -> Node { - Node::TokenEdge(edge.from, edge.token) +// fn pseudo_node(edge: Edge) -> Node { +// Node::TokenEdge(edge.from, edge.token) +// } + +fn balance_node(edge: &Edge) -> Node { + Node::BalanceNode(edge.from, edge.token) } -fn source_address_of(node: &Node) -> &Address { - match node { - Node::Node(addr) => addr, - Node::TokenEdge(from, _) => from, - } +fn trust_node(edge: &Edge) -> Node { + Node::TrustNode(edge.to, edge.token) } +// fn source_address_of(node: &Node) -> &Address { +// match node { +// Node::Node(addr) => addr, +// Node::TokenEdge(from, _) => from, +// } +// } + impl<'a> Adjacencies<'a> { pub fn new(edges: &'a EdgeDB) -> Self { Adjacencies { @@ -70,13 +78,13 @@ impl<'a> Adjacencies<'a> { .or_insert_with(|| { let mut result: HashMap = HashMap::new(); // Plain edges are (from, to, token) labeled with capacity - for edge in self.edges.outgoing(source_address_of(from)) { - match from { - Node::Node(_) => { + match from { + Node::Node(from) => { + for edge in self.edges.outgoing(from) { // One edge from "from" to "from x token" with a capacity // as the max over all "to" addresses (the balance of the sender) result - .entry(pseudo_node(*edge)) + .entry(balance_node(edge)) .and_modify(|c| { if edge.capacity > *c { *c = edge.capacity; @@ -84,14 +92,31 @@ impl<'a> Adjacencies<'a> { }) .or_insert(edge.capacity); } - Node::TokenEdge(_, _) => { - // Another edge from "from x token" to "to" with its - // own capacity (based on the trust) - if pseudo_node(*edge) == *from { - result.insert(Node::Node(edge.to), edge.capacity); + } + Node::BalanceNode(from, token) => { + for edge in self.edges.outgoing(from) { + // The actual capacity of the edge / the send limit. + if edge.from == *from && edge.token == *token { + result.insert(trust_node(edge), edge.capacity); } } } + Node::TrustNode(to, token) => { + let is_return_to_owner = *to == *token; + // If token is to's token: send back to owner, infinite capacity. + // Otherwise, the max of the incoming edges (the trust limit) + let mut capacity = U256::from(0); + for edge in self.edges.incoming(to) { + if edge.token == *token { + if is_return_to_owner { + capacity += edge.capacity + } else { + capacity = max(capacity, edge.capacity) + } + } + result.insert(Node::Node(*to), capacity); + } + } } result }) diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 468d543..11fae9c 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -1,5 +1,5 @@ use crate::graph::adjacencies::Adjacencies; -use crate::graph::{node_as_address, node_as_token_edge, Node}; +use crate::graph::{as_trust_node, Node}; use crate::types::edge::EdgeDB; use crate::types::{Address, Edge, U256}; use std::cmp::min; @@ -117,7 +117,8 @@ fn augmenting_path( queue.push_back((Node::Node(*source), (0, U256::default() - U256::from(1)))); while let Some((node, (depth, flow))) = queue.pop_front() { if let Some(max) = max_distance { - if depth >= max { + // * 3 because we have three edges per trust connection (two intermediate nodes). + if depth >= max * 3 { continue; } } @@ -416,12 +417,12 @@ fn extract_transfers( .and_modify(|balance| *balance -= edge.capacity); *account_balances.entry(edge.to).or_default() += edge.capacity; account_balances.retain(|_account, balance| balance > &mut U256::from(0)); - assert!(used_edges.contains_key(&Node::TokenEdge(edge.from, edge.token))); + assert!(used_edges.contains_key(&Node::BalanceNode(edge.from, edge.token))); used_edges - .entry(Node::TokenEdge(edge.from, edge.token)) + .entry(Node::BalanceNode(edge.from, edge.token)) .and_modify(|outgoing| { - assert!(outgoing.contains_key(&Node::Node(edge.to))); - outgoing.remove(&Node::Node(edge.to)); + assert!(outgoing.contains_key(&Node::TrustNode(edge.to, edge.token))); + outgoing.remove(&Node::TrustNode(edge.to, edge.token)); }); transfers.push(edge); } @@ -439,12 +440,12 @@ fn next_full_capacity_edge( .unwrap_or(&HashMap::new()) .keys() { - for (to_node, capacity) in &used_edges[intermediate] { - let (from, token) = node_as_token_edge(intermediate); + for (trust_node, capacity) in &used_edges[intermediate] { + let (to, token) = as_trust_node(trust_node); if *balance >= *capacity { return Edge { - from: *from, - to: *node_as_address(to_node), + from: *account, + to: *to, token: *token, capacity: *capacity, }; @@ -623,4 +624,44 @@ mod test { ) ); } + + #[test] + fn trust_transfer_limit() { + let (a, b, c, d, t1, t2) = addresses(); + let edges = build_edges(vec![ + // The following two edges should be balance-limited, + // i.e. a -> first intermediate is limited by the max of the two. + Edge { + from: a, + to: b, + token: a, + capacity: U256::from(10), + }, + Edge { + from: a, + to: c, + token: a, + capacity: U256::from(11), + }, + // The following two edges should be trust-limited, + // i.e. the edge from the second (pre-) intermediate is limited + // by the max of the two. + Edge { + from: b, + to: d, + token: a, + capacity: U256::from(9), + }, + Edge { + from: c, + to: d, + token: a, + capacity: U256::from(8), + }, + ]); + let mut flow = compute_flow(&a, &d, &edges, U256::MAX, None); + flow.1.sort(); + println!("{:?}", &flow.1); + assert_eq!(flow.0, U256::from(9)); + } } diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 80f7308..4bf332a 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -4,10 +4,25 @@ use std::fmt::{Display, Formatter}; mod adjacencies; mod flow; +// An edge from the capacity network is +// from, token, to -> capacity +// +// In the transformation into the flow network, we add two intermediate nodes +// per edge that are potentially shared with other edges: +// +// from -A-> BalanceNode(from, token) -B-> TrustNode(to, token) -C-> to +// +// The capacities (A, B, C) are as follows: +// A: the max of all capacity-netwok edges of the form (from, token, *), or A's balance of "token" tokens. +// B: the actual capacity of the capacity-network edge (from, token, to), or the "send limit" from "from" to "to" in "token" tokens +// C: if "token" is C's token (this is a "send to owner" edge): infinity or the sum of all incoming edges. +// otherwise: the max of all capacity-network edges of the form (*, token, to) or the trust limit of "to" for "token" tokens. + #[derive(Debug, Eq, PartialEq, Hash, Clone)] pub enum Node { Node(Address), - TokenEdge(Address, Address), + BalanceNode(Address, Address), + TrustNode(Address, Address), } pub fn node_as_address(node: &Node) -> &Address { @@ -18,9 +33,9 @@ pub fn node_as_address(node: &Node) -> &Address { } } -pub fn node_as_token_edge(node: &Node) -> (&Address, &Address) { - if let Node::TokenEdge(from, token) = node { - (from, token) +pub fn as_trust_node(node: &Node) -> (&Address, &Address) { + if let Node::TrustNode(to, token) = node { + (to, token) } else { panic!() } @@ -30,7 +45,8 @@ impl Display for Node { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { Node::Node(address) => write!(f, "{address}"), - Node::TokenEdge(address, token) => write!(f, "({address} x {token})"), + Node::BalanceNode(from, token) => write!(f, "(bal {from} x {token})"), + Node::TrustNode(to, token) => write!(f, "(trust {to} x {token})"), } } } diff --git a/src/types/edge.rs b/src/types/edge.rs index 07b332d..b2ba1f7 100644 --- a/src/types/edge.rs +++ b/src/types/edge.rs @@ -62,6 +62,17 @@ impl EdgeDB { } } + pub fn incoming(&self, to: &Address) -> Vec<&Edge> { + match self.incoming.get(to) { + Some(incoming) => incoming + .iter() + .map(|i| self.edges.get(*i).unwrap()) + .filter(|e| e.capacity != U256::from(0)) + .collect(), + None => vec![], + } + } + fn index_of(&self, e: &Edge) -> Option { self.outgoing.get(&e.from).and_then(|out| { for i in out {