Make flow computation deterministic.

This commit is contained in:
chriseth 2022-12-28 00:15:06 +01:00
parent 7821daf532
commit e06ce20a8f
3 changed files with 32 additions and 25 deletions

View file

@ -49,7 +49,7 @@ impl<'a> Adjacencies<'a> {
.into_iter() .into_iter()
.filter(|(_, cap)| *cap != U256::from(0)) .filter(|(_, cap)| *cap != U256::from(0))
.collect::<Vec<(Node, U256)>>(); .collect::<Vec<(Node, U256)>>();
result.sort_unstable_by_key(|(_, capacity)| Reverse(*capacity)); result.sort_unstable_by_key(|(addr, capacity)| (Reverse(*capacity), addr.clone()));
result result
} }

View file

@ -241,7 +241,9 @@ fn reduce_transfers(
if all_edges.clone().count() <= max_transfers as usize { if all_edges.clone().count() <= max_transfers as usize {
return reduced_flow; return reduced_flow;
} }
let ((f, t), c) = all_edges.min_by_key(|(_, c)| *c).unwrap(); let ((f, t), c) = all_edges
.min_by_key(|(addr, c)| (*c, addr.clone()))
.unwrap();
reduced_flow += *c; reduced_flow += *c;
prune_edge(used_edges, (&f, &t), *c); prune_edge(used_edges, (&f, &t), *c);
} }
@ -334,7 +336,7 @@ fn smallest_edge_in_set(
(a, b, capacity) (a, b, capacity)
}) })
.filter(|(_, _, capacity)| capacity.is_some()) .filter(|(_, _, capacity)| capacity.is_some())
.min_by_key(|(_, _, capacity)| capacity.unwrap()) .min_by_key(|(a, b, capacity)| (capacity.unwrap(), *a, *b))
{ {
Some((a.clone(), b.clone())) Some((a.clone(), b.clone()))
} else { } else {
@ -348,9 +350,9 @@ fn smallest_edge_from(
) -> Option<(Node, U256)> { ) -> Option<(Node, U256)> {
used_edges.get(n).and_then(|out| { used_edges.get(n).and_then(|out| {
out.iter() out.iter()
.min_by_key(|(_, c)| { .min_by_key(|(addr, c)| {
assert!(**c != U256::from(0)); assert!(**c != U256::from(0));
*c (*c, *addr)
}) })
.map(|(t, c)| (t.clone(), *c)) .map(|(t, c)| (t.clone(), *c))
}) })
@ -364,9 +366,9 @@ fn smallest_edge_to(
.iter() .iter()
.filter(|(_, out)| out.contains_key(n)) .filter(|(_, out)| out.contains_key(n))
.map(|(t, out)| (t, out[n])) .map(|(t, out)| (t, out[n]))
.min_by_key(|(_, c)| { .min_by_key(|(addr, c)| {
assert!(*c != U256::from(0)); assert!(*c != U256::from(0));
*c (*c, *addr)
}) })
.map(|(t, c)| (t.clone(), c)) .map(|(t, c)| (t.clone(), c))
} }
@ -433,7 +435,7 @@ fn extract_transfers(
mut used_edges: HashMap<Node, HashMap<Node, U256>>, mut used_edges: HashMap<Node, HashMap<Node, U256>>,
) -> Vec<Edge> { ) -> Vec<Edge> {
let mut transfers: Vec<Edge> = Vec::new(); let mut transfers: Vec<Edge> = Vec::new();
let mut account_balances: HashMap<Address, U256> = HashMap::new(); let mut account_balances: BTreeMap<Address, U256> = BTreeMap::new();
account_balances.insert(*source, *amount); account_balances.insert(*source, *amount);
while !account_balances.is_empty() while !account_balances.is_empty()
@ -461,25 +463,30 @@ fn extract_transfers(
fn next_full_capacity_edge( fn next_full_capacity_edge(
used_edges: &HashMap<Node, HashMap<Node, U256>>, used_edges: &HashMap<Node, HashMap<Node, U256>>,
account_balances: &HashMap<Address, U256>, account_balances: &BTreeMap<Address, U256>,
) -> Edge { ) -> Edge {
for (account, balance) in account_balances { for (account, balance) in account_balances {
for intermediate in used_edges let edge = used_edges
.get(&Node::Node(*account)) .get(&Node::Node(*account))
.unwrap_or(&HashMap::new()) .map(|v| {
.keys() v.keys().flat_map(|intermediate| {
{ used_edges[intermediate]
for (trust_node, capacity) in &used_edges[intermediate] { .iter()
let (to, token) = as_trust_node(trust_node); .filter(|(_, capacity)| *balance >= **capacity)
if *balance >= *capacity { .map(|(trust_node, capacity)| {
return Edge { let (to, token) = as_trust_node(trust_node);
from: *account, Edge {
to: *to, from: *account,
token: *token, to: *to,
capacity: *capacity, token: *token,
}; capacity: *capacity,
} }
} })
})
})
.and_then(|edges| edges.min());
if let Some(edge) = edge {
return edge;
} }
} }
panic!(); panic!();

View file

@ -18,7 +18,7 @@ mod flow;
// C: if "token" is C's token (this is a "send to owner" edge): infinity or the sum of all incoming edges. // 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. // 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, PartialOrd, Ord)]
pub enum Node { pub enum Node {
Node(Address), Node(Address),
BalanceNode(Address, Address), BalanceNode(Address, Address),