Fix trust limit problem.
This commit is contained in:
parent
0fed55f154
commit
f37b35d6ec
4 changed files with 126 additions and 33 deletions
|
@ -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<Node, HashMap<Node, U256>>,
|
||||
}
|
||||
|
||||
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<Node, U256> = 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
|
||||
})
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
self.outgoing.get(&e.from).and_then(|out| {
|
||||
for i in out {
|
||||
|
|
Loading…
Reference in a new issue