Fix trust limit problem.

This commit is contained in:
chriseth 2022-12-06 21:23:38 +01:00
parent 0fed55f154
commit f37b35d6ec
4 changed files with 126 additions and 33 deletions

View file

@ -1,7 +1,7 @@
use crate::graph::Node; use crate::graph::Node;
use crate::types::edge::EdgeDB; use crate::types::edge::EdgeDB;
use crate::types::{Address, Edge, U256}; use crate::types::{Edge, U256};
use std::cmp::Reverse; use std::cmp::{max, Reverse};
use std::collections::HashMap; use std::collections::HashMap;
pub struct Adjacencies<'a> { pub struct Adjacencies<'a> {
@ -10,17 +10,25 @@ pub struct Adjacencies<'a> {
capacity_adjustments: HashMap<Node, HashMap<Node, U256>>, capacity_adjustments: HashMap<Node, HashMap<Node, U256>>,
} }
fn pseudo_node(edge: Edge) -> Node { // fn pseudo_node(edge: Edge) -> Node {
Node::TokenEdge(edge.from, edge.token) // 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 { fn trust_node(edge: &Edge) -> Node {
match node { Node::TrustNode(edge.to, edge.token)
Node::Node(addr) => addr,
Node::TokenEdge(from, _) => from,
}
} }
// fn source_address_of(node: &Node) -> &Address {
// match node {
// Node::Node(addr) => addr,
// Node::TokenEdge(from, _) => from,
// }
// }
impl<'a> Adjacencies<'a> { impl<'a> Adjacencies<'a> {
pub fn new(edges: &'a EdgeDB) -> Self { pub fn new(edges: &'a EdgeDB) -> Self {
Adjacencies { Adjacencies {
@ -70,13 +78,13 @@ impl<'a> Adjacencies<'a> {
.or_insert_with(|| { .or_insert_with(|| {
let mut result: HashMap<Node, U256> = HashMap::new(); let mut result: HashMap<Node, U256> = HashMap::new();
// Plain edges are (from, to, token) labeled with capacity // Plain edges are (from, to, token) labeled with capacity
for edge in self.edges.outgoing(source_address_of(from)) { match from {
match from { Node::Node(from) => {
Node::Node(_) => { for edge in self.edges.outgoing(from) {
// One edge from "from" to "from x token" with a capacity // One edge from "from" to "from x token" with a capacity
// as the max over all "to" addresses (the balance of the sender) // as the max over all "to" addresses (the balance of the sender)
result result
.entry(pseudo_node(*edge)) .entry(balance_node(edge))
.and_modify(|c| { .and_modify(|c| {
if edge.capacity > *c { if edge.capacity > *c {
*c = edge.capacity; *c = edge.capacity;
@ -84,14 +92,31 @@ impl<'a> Adjacencies<'a> {
}) })
.or_insert(edge.capacity); .or_insert(edge.capacity);
} }
Node::TokenEdge(_, _) => { }
// Another edge from "from x token" to "to" with its Node::BalanceNode(from, token) => {
// own capacity (based on the trust) for edge in self.edges.outgoing(from) {
if pseudo_node(*edge) == *from { // The actual capacity of the edge / the send limit.
result.insert(Node::Node(edge.to), edge.capacity); 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 result
}) })

View file

@ -1,5 +1,5 @@
use crate::graph::adjacencies::Adjacencies; 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::edge::EdgeDB;
use crate::types::{Address, Edge, U256}; use crate::types::{Address, Edge, U256};
use std::cmp::min; use std::cmp::min;
@ -117,7 +117,8 @@ fn augmenting_path(
queue.push_back((Node::Node(*source), (0, U256::default() - U256::from(1)))); queue.push_back((Node::Node(*source), (0, U256::default() - U256::from(1))));
while let Some((node, (depth, flow))) = queue.pop_front() { while let Some((node, (depth, flow))) = queue.pop_front() {
if let Some(max) = max_distance { 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; continue;
} }
} }
@ -416,12 +417,12 @@ fn extract_transfers(
.and_modify(|balance| *balance -= edge.capacity); .and_modify(|balance| *balance -= edge.capacity);
*account_balances.entry(edge.to).or_default() += edge.capacity; *account_balances.entry(edge.to).or_default() += edge.capacity;
account_balances.retain(|_account, balance| balance > &mut U256::from(0)); 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 used_edges
.entry(Node::TokenEdge(edge.from, edge.token)) .entry(Node::BalanceNode(edge.from, edge.token))
.and_modify(|outgoing| { .and_modify(|outgoing| {
assert!(outgoing.contains_key(&Node::Node(edge.to))); assert!(outgoing.contains_key(&Node::TrustNode(edge.to, edge.token)));
outgoing.remove(&Node::Node(edge.to)); outgoing.remove(&Node::TrustNode(edge.to, edge.token));
}); });
transfers.push(edge); transfers.push(edge);
} }
@ -439,12 +440,12 @@ fn next_full_capacity_edge(
.unwrap_or(&HashMap::new()) .unwrap_or(&HashMap::new())
.keys() .keys()
{ {
for (to_node, capacity) in &used_edges[intermediate] { for (trust_node, capacity) in &used_edges[intermediate] {
let (from, token) = node_as_token_edge(intermediate); let (to, token) = as_trust_node(trust_node);
if *balance >= *capacity { if *balance >= *capacity {
return Edge { return Edge {
from: *from, from: *account,
to: *node_as_address(to_node), to: *to,
token: *token, token: *token,
capacity: *capacity, 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));
}
} }

View file

@ -4,10 +4,25 @@ use std::fmt::{Display, Formatter};
mod adjacencies; mod adjacencies;
mod flow; 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)] #[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub enum Node { pub enum Node {
Node(Address), Node(Address),
TokenEdge(Address, Address), BalanceNode(Address, Address),
TrustNode(Address, Address),
} }
pub fn node_as_address(node: &Node) -> &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) { pub fn as_trust_node(node: &Node) -> (&Address, &Address) {
if let Node::TokenEdge(from, token) = node { if let Node::TrustNode(to, token) = node {
(from, token) (to, token)
} else { } else {
panic!() panic!()
} }
@ -30,7 +45,8 @@ impl Display for Node {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self { match self {
Node::Node(address) => write!(f, "{address}"), 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})"),
} }
} }
} }

View file

@ -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<usize> { fn index_of(&self, e: &Edge) -> Option<usize> {
self.outgoing.get(&e.from).and_then(|out| { self.outgoing.get(&e.from).and_then(|out| {
for i in out { for i in out {