From 3054a240bb3d086c64e5a7a6ba709ac4c4566ffd Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 15 Dec 2018 16:54:01 +0000 Subject: [PATCH] Sybil: More refactoring to separate analysis and display --- .../java/src/net/i2p/router/sybil/Pair.java | 24 + .../i2p/router/web/helpers/SybilRenderer.java | 527 +++++++++++------- 2 files changed, 339 insertions(+), 212 deletions(-) create mode 100644 apps/routerconsole/java/src/net/i2p/router/sybil/Pair.java diff --git a/apps/routerconsole/java/src/net/i2p/router/sybil/Pair.java b/apps/routerconsole/java/src/net/i2p/router/sybil/Pair.java new file mode 100644 index 000000000..adfe81470 --- /dev/null +++ b/apps/routerconsole/java/src/net/i2p/router/sybil/Pair.java @@ -0,0 +1,24 @@ +package net.i2p.router.sybil; + +import java.math.BigInteger; + +import net.i2p.data.router.RouterInfo; + +/** + * A pair of routers and the distance between them. + * + * @since 0.9.38 moved from SybilRenderer + */ +public class Pair implements Comparable { + public final RouterInfo r1, r2; + public final BigInteger dist; + + public Pair(RouterInfo ri1, RouterInfo ri2, BigInteger distance) { + r1 = ri1; r2 = ri2; dist = distance; + } + + public int compareTo(Pair p) { + return this.dist.compareTo(p.dist); + } +} + diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java index 7dab4226d..ab0644938 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java @@ -30,6 +30,7 @@ import net.i2p.router.TunnelPoolSettings; import net.i2p.router.crypto.FamilyKeyCrypto; import net.i2p.router.peermanager.DBHistory; import net.i2p.router.peermanager.PeerProfile; +import net.i2p.router.sybil.Pair; import net.i2p.router.sybil.Points; import net.i2p.router.tunnel.pool.TunnelPool; import net.i2p.router.util.HashDistance; @@ -247,27 +248,40 @@ public class SybilRenderer { Map points = new HashMap(64); // IP analysis - renderIPGroupsFamily(out, buf, ris, points); - renderIPGroupsUs(out, buf, ris, points); - renderIPGroups32(out, buf, ris, points); - renderIPGroups24(out, buf, ris, points); - renderIPGroups16(out, buf, ris, points); + Map> fmap = calculateIPGroupsFamily(ris, points); + renderIPGroupsFamily(out, buf, fmap); + List ri32 = new ArrayList(4); + List ri24 = new ArrayList(4); + List ri16 = new ArrayList(4); + calculateIPGroupsUs(ris, points, ri32, ri24, ri16); + renderIPGroupsUs(out, buf, ri32, ri24, ri16); + Map> map = calculateIPGroups32(ris, points); + renderIPGroups32(out, buf, map); + map = calculateIPGroups24(ris, points); + renderIPGroups24(out, buf, map); + map = calculateIPGroups16(ris, points); + //renderIPGroups16(out, buf, map); // Pairwise distance analysis - renderPairDistance(out, buf, ris, points); + List pairs = new ArrayList(PAIRMAX); + double avg = calculatePairDistance(ris, points, pairs); + renderPairDistance(out, buf, pairs, avg); // Distance to our router analysis buf.append("

Closest Floodfills to Our Routing Key (Where we Store our RI)

"); buf.append("

See all

"); - renderRouterInfoHTML(out, buf, ourRKey, "our rkey", avgMinDist, ris, points); + calculateRouterInfo(ourRKey, "our rkey", ris, points); + renderRouterInfoHTML(out, buf, ourRKey, avgMinDist, ris); RouterKeyGenerator rkgen = _context.routerKeyGenerator(); Hash nkey = rkgen.getNextRoutingKey(us); buf.append("

Closest Floodfills to Tomorrow's Routing Key (Where we will Store our RI)

"); buf.append("

See all

"); - renderRouterInfoHTML(out, buf, nkey, "our rkey (tomorrow)", avgMinDist, ris, points); + calculateRouterInfo(nkey, "our rkey (tomorrow)", ris, points); + renderRouterInfoHTML(out, buf, nkey, avgMinDist, ris); buf.append("

Closest Floodfills to Our Router Hash (DHT Neighbors if we are Floodfill)

"); - renderRouterInfoHTML(out, buf, us, "our router", avgMinDist, ris, points); + calculateRouterInfo(us, "our router", ris, points); + renderRouterInfoHTML(out, buf, us, avgMinDist, ris); // Distance to our published destinations analysis buf.append("

Floodfills Close to Our Destinations

"); @@ -288,11 +302,13 @@ public class SybilRenderer { String name = (in != null) ? DataHelper.escapeHTML(in.getSettings().getDestinationNickname()) : client.toBase64().substring(0,4); buf.append("

Closest floodfills to the Routing Key for " + name + " (where we store our LS)

"); buf.append("

See all

"); - renderRouterInfoHTML(out, buf, rkey, name, avgMinDist, ris, points); + calculateRouterInfo(rkey, name, ris, points); + renderRouterInfoHTML(out, buf, rkey, avgMinDist, ris); nkey = rkgen.getNextRoutingKey(ls.getHash()); buf.append("

Closest floodfills to Tomorrow's Routing Key for " + name + " (where we will store our LS)

"); buf.append("

See all

"); - renderRouterInfoHTML(out, buf, nkey, name + " (tomorrow)", avgMinDist, ris, points); + calculateRouterInfo(nkey, name + " (tomorrow)", ris, points); + renderRouterInfoHTML(out, buf, nkey, avgMinDist, ris); } // Profile analysis @@ -344,25 +360,29 @@ public class SybilRenderer { Map points = new HashMap(64); // IP analysis - renderIPGroupsFamily(null, null, ris, points); - renderIPGroupsUs(null, null, ris, points); - renderIPGroups32(null, null, ris, points); - renderIPGroups24(null, null, ris, points); - renderIPGroups16(null, null, ris, points); + calculateIPGroupsFamily(ris, points); + List ri32 = new ArrayList(4); + List ri24 = new ArrayList(4); + List ri16 = new ArrayList(4); + calculateIPGroupsUs(ris, points, ri32, ri24, ri16); + calculateIPGroups32(ris, points); + calculateIPGroups24(ris, points); + calculateIPGroups16(ris, points); // Pairwise distance analysis - renderPairDistance(null, null, ris, points); + List pairs = new ArrayList(PAIRMAX); + calculatePairDistance(ris, points, pairs); // Distance to our router analysis // closest to our routing key today Hash ourRKey = _context.router().getRouterInfo().getRoutingKey(); - renderRouterInfoHTML(null, null, ourRKey, "our rkey", avgMinDist, ris, points); + calculateRouterInfo(ourRKey, "our rkey", ris, points); // closest to our routing key tomorrow RouterKeyGenerator rkgen = _context.routerKeyGenerator(); Hash nkey = rkgen.getNextRoutingKey(us); - renderRouterInfoHTML(null, null, nkey, "our rkey (tomorrow)", avgMinDist, ris, points); + calculateRouterInfo(nkey, "our rkey (tomorrow)", ris, points); // closest to us - renderRouterInfoHTML(null, null, us, "our router", avgMinDist, ris, points); + calculateRouterInfo(us, "our router", ris, points); // Distance to our published destinations analysis Map clientInboundPools = _context.tunnelManager().getInboundClientPools(); @@ -380,10 +400,10 @@ public class SybilRenderer { TunnelPool in = clientInboundPools.get(client); String name = (in != null) ? DataHelper.escapeHTML(in.getSettings().getDestinationNickname()) : client.toBase64().substring(0,4); // closest to routing key today - renderRouterInfoHTML(null, null, rkey, name, avgMinDist, ris, points); + calculateRouterInfo(rkey, name, ris, points); // closest to routing key tomorrow nkey = rkgen.getNextRoutingKey(ls.getHash()); - renderRouterInfoHTML(null, null, nkey, name + " (tomorrow)", avgMinDist, ris, points); + calculateRouterInfo(nkey, name + " (tomorrow)", ris, points); } // Profile analysis @@ -392,24 +412,14 @@ public class SybilRenderer { return points; } - private static class Pair implements Comparable { - public final RouterInfo r1, r2; - public final BigInteger dist; - public Pair(RouterInfo ri1, RouterInfo ri2, BigInteger distance) { - r1 = ri1; r2 = ri2; dist = distance; - } - public int compareTo(Pair p) { - return this.dist.compareTo(p.dist); - } - } - /** - * @param out null for background analysis - * @param buf null for background analysis + * @param pairs out parameter, sorted + * @param return average distance + * @since 0.9.38 split out from renderPairDistance() */ - private void renderPairDistance(Writer out, StringBuilder buf, List ris, Map points) throws IOException { + private double calculatePairDistance(List ris, Map points, + List pairs) { int sz = ris.size(); - List pairs = new ArrayList(PAIRMAX); double total = 0; for (int i = 0; i < sz; i++) { RouterInfo info1 = ris.get(i); @@ -430,22 +440,11 @@ public class SybilRenderer { } double avg = total / (sz * sz / 2d); - if (buf != null) { - buf.append("

Average Floodfill Distance is ").append(fmt.format(avg)).append("

" + - "

Closest Floodfill Pairs by Hash

"); - } for (Pair p : pairs) { double distance = biLog2(p.dist); double point = MIN_CLOSE - distance; if (point < 0) break; // sorted; - if (buf != null && point >= 2) { - // limit display - buf.append("

Hash Distance: ").append(fmt.format(distance)).append(": " + - "

"); - renderRouterInfo(buf, p.r1, null, false, false); - renderRouterInfo(buf, p.r2, null, false, false); - } point *= PAIR_DISTANCE_FACTOR; String b2 = p.r2.getHash().toBase64(); addPoints(points, p.r1.getHash(), point, "Very close (" + fmt.format(distance) + @@ -454,11 +453,31 @@ public class SybilRenderer { addPoints(points, p.r2.getHash(), point, "Very close (" + fmt.format(distance) + ") to other floodfill " + b1 + ""); } - if (buf != null) { - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + return avg; + } + + /** + * @param pairs sorted + */ + private void renderPairDistance(Writer out, StringBuilder buf, List pairs, double avg) throws IOException { + buf.append("

Average Floodfill Distance is ").append(fmt.format(avg)).append("

" + + "

Closest Floodfill Pairs by Hash

"); + + for (Pair p : pairs) { + double distance = biLog2(p.dist); + double point = MIN_CLOSE - distance; + // limit display + if (point < 2) + break; // sorted; + buf.append("

Hash Distance: ").append(fmt.format(distance)).append("" + + "

"); + renderRouterInfo(buf, p.r1, null, false, false); + renderRouterInfo(buf, p.r2, null, false, false); } + + out.write(buf.toString()); + out.flush(); + buf.setLength(0); } private double closestDistance(Hash h, List ris) { @@ -482,11 +501,11 @@ public class SybilRenderer { } private static class FooComparator implements Comparator, Serializable { - private final ObjectCounter _o; - public FooComparator(ObjectCounter o) { _o = o;} + private final Map> _o; + public FooComparator(Map> o) { _o = o;} public int compare(Integer l, Integer r) { // reverse by count - int rv = _o.count(r) - _o.count(l); + int rv = _o.get(r).size() - _o.get(l).size(); if (rv != 0) return rv; // foward by IP @@ -495,12 +514,12 @@ public class SybilRenderer { } private static class FoofComparator implements Comparator, Serializable { - private final ObjectCounter _o; + private final Map> _o; private final Collator _comp = Collator.getInstance(); - public FoofComparator(ObjectCounter o) { _o = o;} + public FoofComparator(Map> o) { _o = o;} public int compare(String l, String r) { // reverse by count - int rv = _o.count(r) - _o.count(l); + int rv = _o.get(r).size() - _o.get(l).size(); if (rv != 0) return rv; // foward by name @@ -509,62 +528,74 @@ public class SybilRenderer { } /** - * @param out null for background analysis - * @param buf null for background analysis + * @param ri32 out parameter + * @param ri24 out parameter + * @param ri16 out parameter + * @since 0.9.38 split out from renderIPGroupsUs() */ - private void renderIPGroupsUs(Writer out, StringBuilder buf, List ris, Map points) throws IOException { + private void calculateIPGroupsUs(List ris, Map points, + List ri32, List ri24, List ri16) { RouterInfo us = _context.router().getRouterInfo(); byte[] ourIP = getIP(us); if (ourIP == null) return; - if (buf != null) - buf.append("

Floodfills close to Our IP

"); - boolean found = false; for (RouterInfo info : ris) { byte[] ip = getIP(info); if (ip == null) continue; if (ip[0] == ourIP[0] && ip[1] == ourIP[1]) { - if (buf != null) - buf.append("

"); if (ip[2] == ourIP[2]) { if (ip[3] == ourIP[3]) { - if (buf != null) - buf.append("Same IP as us"); addPoints(points, info.getHash(), POINTS_US32, "Same IP as us"); } else { - if (buf != null) - buf.append("Same /24 as us"); addPoints(points, info.getHash(), POINTS_US24, "Same /24 as us"); } } else { - if (buf != null) - buf.append("Same /16 as us"); addPoints(points, info.getHash(), POINTS_US16, "Same /16 as us"); } - if (buf != null) { - buf.append(":

"); - renderRouterInfo(buf, info, null, false, false); - } - found = true; } } - if (buf != null) { - if (!found) - buf.append("

None

"); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); - } } /** - * @param out null for background analysis - * @param buf null for background analysis + * */ - private void renderIPGroups32(Writer out, StringBuilder buf, List ris, Map points) throws IOException { - if (buf != null) - buf.append("

Floodfills with the Same IP

"); + private void renderIPGroupsUs(Writer out, StringBuilder buf, List ri32, + List ri24, List ri16) throws IOException { + buf.append("

Floodfills close to Our IP

"); + boolean found = false; + for (RouterInfo info : ri32) { + buf.append("

"); + buf.append("Same IP as us"); + buf.append(":

"); + renderRouterInfo(buf, info, null, false, false); + found = true; + } + for (RouterInfo info : ri24) { + buf.append("

"); + buf.append("Same /24 as us"); + buf.append(":

"); + renderRouterInfo(buf, info, null, false, false); + found = true; + } + for (RouterInfo info : ri16) { + buf.append("

"); + buf.append("Same /16 as us"); + buf.append(":

"); + renderRouterInfo(buf, info, null, false, false); + found = true; + } + if (!found) + buf.append("

None

"); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } + + /** + * @since 0.9.38 split out from renderIPGroups32() + */ + private Map> calculateIPGroups32(List ris, Map points) { ObjectCounter oc = new ObjectCounter(); for (RouterInfo info : ris) { byte[] ip = getIP(info); @@ -573,27 +604,20 @@ public class SybilRenderer { Integer x = Integer.valueOf((int) DataHelper.fromLong(ip, 0, 4)); oc.increment(x); } - List foo = new ArrayList(); + Map> rv = new HashMap>(); for (Integer ii : oc.objects()) { int count = oc.count(ii); if (count >= 2) - foo.add(ii); + rv.put(ii, new ArrayList(4)); } - Collections.sort(foo, new FooComparator(oc)); - boolean found = false; - for (Integer ii : foo) { + for (Map.Entry> e : rv.entrySet()) { + Integer ii = e.getKey(); int count = oc.count(ii); int i = ii.intValue(); int i0 = (i >> 24) & 0xff; int i1 = (i >> 16) & 0xff; int i2 = (i >> 8) & 0xff; int i3 = i & 0xff; - String sip = i0 + "." + i1 + '.' + i2 + '.' + i3; - if (buf != null) { - buf.append("

").append(count).append(" floodfills with IP ").append(sip) - .append(":

"); - } for (RouterInfo info : ris) { byte[] ip = getIP(info); if (ip == null) @@ -606,29 +630,50 @@ public class SybilRenderer { continue; if ((ip[3] & 0xff) != i3) continue; - found = true; - if (buf != null) - renderRouterInfo(buf, info, null, false, false); + e.getValue().add(info); double point = POINTS32 * (count - 1); addPoints(points, info.getHash(), point, "Same IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "")); } } - if (buf != null) { - if (!found) - buf.append("

None

"); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); - } + return rv; } /** - * @param out null for background analysis - * @param buf null for background analysis + * */ - private void renderIPGroups24(Writer out, StringBuilder buf, List ris, Map points) throws IOException { - if (buf != null) - buf.append("

Floodfills in the Same /24 (2 minimum)

"); + private void renderIPGroups32(Writer out, StringBuilder buf, Map> map) throws IOException { + buf.append("

Floodfills with the Same IP

"); + List foo = new ArrayList(map.keySet()); + Collections.sort(foo, new FooComparator(map)); + boolean found = false; + for (Integer ii : foo) { + List ris = map.get(ii); + int count = ris.size(); + int i = ii.intValue(); + int i0 = (i >> 24) & 0xff; + int i1 = (i >> 16) & 0xff; + int i2 = (i >> 8) & 0xff; + int i3 = i & 0xff; + String sip = i0 + "." + i1 + '.' + i2 + '.' + i3; + buf.append("

").append(count).append(" floodfills with IP ").append(sip) + .append(":

"); + for (RouterInfo info : ris) { + found = true; + renderRouterInfo(buf, info, null, false, false); + } + } + if (!found) + buf.append("

None

"); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } + + /** + * @since 0.9.38 split out from renderIPGroups24() + */ + private Map> calculateIPGroups24(List ris, Map points) { ObjectCounter oc = new ObjectCounter(); for (RouterInfo info : ris) { byte[] ip = getIP(info); @@ -637,26 +682,19 @@ public class SybilRenderer { Integer x = Integer.valueOf((int) DataHelper.fromLong(ip, 0, 3)); oc.increment(x); } - List foo = new ArrayList(); + Map> rv = new HashMap>(); for (Integer ii : oc.objects()) { int count = oc.count(ii); if (count >= 2) - foo.add(ii); + rv.put(ii, new ArrayList(4)); } - Collections.sort(foo, new FooComparator(oc)); - boolean found = false; - for (Integer ii : foo) { + for (Map.Entry> e : rv.entrySet()) { + Integer ii = e.getKey(); int count = oc.count(ii); int i = ii.intValue(); int i0 = i >> 16; int i1 = (i >> 8) & 0xff; int i2 = i & 0xff; - String sip = i0 + "." + i1 + '.' + i2 + ".0/24"; - if (buf != null) { - buf.append("

").append(count).append(" floodfills with IP ").append(sip) - .append(":

"); - } for (RouterInfo info : ris) { byte[] ip = getIP(info); if (ip == null) @@ -667,29 +705,49 @@ public class SybilRenderer { continue; if ((ip[2] & 0xff) != i2) continue; - found = true; - if (buf != null) - renderRouterInfo(buf, info, null, false, false); + e.getValue().add(info); double point = POINTS24 * (count - 1); addPoints(points, info.getHash(), point, "Same /24 IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "")); } } - if (buf != null) { - if (!found) - buf.append("

None

"); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); - } + return rv; } /** - * @param out null for background analysis - * @param buf null for background analysis + * */ - private void renderIPGroups16(Writer out, StringBuilder buf, List ris, Map points) throws IOException { - if (buf != null) - buf.append("

Floodfills in the Same /16 (4 minimum)

"); + private void renderIPGroups24(Writer out, StringBuilder buf, Map> map) throws IOException { + buf.append("

Floodfills in the Same /24 (2 minimum)

"); + List foo = new ArrayList(map.keySet()); + Collections.sort(foo, new FooComparator(map)); + boolean found = false; + for (Integer ii : foo) { + List ris = map.get(ii); + int count = ris.size(); + int i = ii.intValue(); + int i0 = i >> 16; + int i1 = (i >> 8) & 0xff; + int i2 = i & 0xff; + String sip = i0 + "." + i1 + '.' + i2 + ".0/24"; + buf.append("

").append(count).append(" floodfills with IP ").append(sip) + .append(":

"); + for (RouterInfo info : ris) { + found = true; + renderRouterInfo(buf, info, null, false, false); + } + } + if (!found) + buf.append("

None

"); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } + + /** + * @since 0.9.38 split out from renderIPGroups16() + */ + private Map> calculateIPGroups16(List ris, Map points) { ObjectCounter oc = new ObjectCounter(); for (RouterInfo info : ris) { byte[] ip = getIP(info); @@ -698,16 +756,45 @@ public class SybilRenderer { Integer x = Integer.valueOf((int) DataHelper.fromLong(ip, 0, 2)); oc.increment(x); } - List foo = new ArrayList(); + Map> rv = new HashMap>(); for (Integer ii : oc.objects()) { int count = oc.count(ii); if (count >= 4) - foo.add(ii); + rv.put(ii, new ArrayList(8)); } - Collections.sort(foo, new FooComparator(oc)); + for (Map.Entry> e : rv.entrySet()) { + Integer ii = e.getKey(); + int count = oc.count(ii); + int i = ii.intValue(); + int i0 = i >> 8; + int i1 = i & 0xff; + for (RouterInfo info : ris) { + byte[] ip = getIP(info); + if (ip == null) + continue; + if ((ip[0] & 0xff) != i0) + continue; + if ((ip[1] & 0xff) != i1) + continue; + e.getValue().add(info); + double point = POINTS16 * (count - 1); + addPoints(points, info.getHash(), point, "Same /16 IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "")); + } + } + return rv; + } + + /** + * + */ + private void renderIPGroups16(Writer out, StringBuilder buf, Map> map) throws IOException { + buf.append("

Floodfills in the Same /16 (4 minimum)

"); + List foo = new ArrayList(map.keySet()); + Collections.sort(foo, new FooComparator(map)); boolean found = false; for (Integer ii : foo) { - int count = oc.count(ii); + List ris = map.get(ii); + int count = ris.size(); int i = ii.intValue(); int i0 = i >> 8; int i1 = i & 0xff; @@ -718,36 +805,21 @@ public class SybilRenderer { .append("

"); } for (RouterInfo info : ris) { - byte[] ip = getIP(info); - if (ip == null) - continue; - if ((ip[0] & 0xff) != i0) - continue; - if ((ip[1] & 0xff) != i1) - continue; found = true; - // limit display - //renderRouterInfo(buf, info, null, false, false); - double point = POINTS16 * (count - 1); - addPoints(points, info.getHash(), point, "Same /16 IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "")); + renderRouterInfo(buf, info, null, false, false); } } - if (buf != null) { - if (!found) - buf.append("

None

"); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); - } + if (!found) + buf.append("

None

"); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); } /** - * @param out null for background analysis - * @param buf null for background analysis + * @since 0.9.38 split out from renderIPGroupsFamily() */ - private void renderIPGroupsFamily(Writer out, StringBuilder buf, List ris, Map points) throws IOException { - if (buf != null) - buf.append("

Floodfills in the same Family

"); + private Map> calculateIPGroupsFamily(List ris, Map points) { ObjectCounter oc = new ObjectCounter(); for (RouterInfo info : ris) { String fam = info.getOption("family"); @@ -756,26 +828,21 @@ public class SybilRenderer { oc.increment(fam); } List foo = new ArrayList(oc.objects()); - Collections.sort(foo, new FoofComparator(oc)); + Map> rv = new HashMap>(foo.size()); FamilyKeyCrypto fkc = _context.router().getFamilyKeyCrypto(); String ourFamily = fkc != null ? fkc.getOurFamilyName() : null; - boolean found = false; for (String s : foo) { int count = oc.count(s); + List list = new ArrayList(count); + rv.put(s, list); String ss = DataHelper.escapeHTML(s); - if (buf != null && count > 1) { - buf.append("

").append(count).append(" floodfills in family:  ").append(ss).append("

"); - } for (RouterInfo info : ris) { String fam = info.getOption("family"); if (fam == null) continue; if (!fam.equals(s)) continue; - found = true; - // limit display - //renderRouterInfo(buf, info, null, false, false); + list.add(info); double point = POINTS_FAMILY; if (fkc != null && s.equals(ourFamily)) { if (fkc.verifyOurFamily(info)) @@ -789,14 +856,39 @@ public class SybilRenderer { } } } - if (buf != null) { - if (!found) - buf.append("

None

"); - buf.append("
"); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + return rv; + } + + /** + * + */ + private void renderIPGroupsFamily(Writer out, StringBuilder buf, Map> map) throws IOException { + buf.append("

Floodfills in the same Family

"); + List foo = new ArrayList(map.keySet()); + Collections.sort(foo, new FoofComparator(map)); + FamilyKeyCrypto fkc = _context.router().getFamilyKeyCrypto(); + String ourFamily = fkc != null ? fkc.getOurFamilyName() : null; + boolean found = false; + for (String s : foo) { + List list = map.get(s); + int count = list.size(); + String ss = DataHelper.escapeHTML(s); + if (count > 1) { + buf.append("

").append(count).append(" floodfills in family:  ").append(ss).append("

"); + found = true; + } + //for (RouterInfo info : ris) { + // limit display + //renderRouterInfo(buf, info, null, false, false); + //} } + if (!found) + buf.append("

None

"); + buf.append("
"); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); } private static final long DAY = 24*60*60*1000L; @@ -878,13 +970,33 @@ public class SybilRenderer { } /** - * @param out null for background analysis - * @param buf null for background analysis + * @param usName HTML escaped + * @param ris will be re-sorted in place + * @since 0.9.38 split out from renderRouterInfoHTML() + */ + private void calculateRouterInfo(Hash us, String usName, + List ris, Map points) { + Collections.sort(ris, new RouterInfoRoutingKeyComparator(us)); + int count = Math.min(MAX, ris.size()); + for (int i = 0; i < count; i++) { + RouterInfo ri = ris.get(i); + BigInteger bidist = HashDistance.getDistance(us, ri.getHash()); + double dist = biLog2(bidist); + double point = MIN_CLOSE - dist; + if (point <= 0) + break; + point *= OUR_KEY_FACTOR; + addPoints(points, ri.getHash(), point, "Very close (" + fmt.format(dist) + ") to our key " + usName + ": " + us.toBase64()); + } + } + + /** + * Render routers closer than MIN_CLOSE up to MAX routers + * @param ris sorted, closest first * @param usName HTML escaped */ - private void renderRouterInfoHTML(Writer out, StringBuilder buf, Hash us, String usName, double avgMinDist, - List ris, Map points) throws IOException { - Collections.sort(ris, new RouterInfoRoutingKeyComparator(us)); + private void renderRouterInfoHTML(Writer out, StringBuilder buf, Hash us, double avgMinDist, + List ris) throws IOException { double min = 256; double max = 0; double tot = 0; @@ -894,14 +1006,10 @@ public class SybilRenderer { int medIdx = isEven ? (count / 2) - 1 : (count / 2); for (int i = 0; i < count; i++) { RouterInfo ri = ris.get(i); - double dist; - if (buf != null) { - dist = renderRouterInfo(buf, ri, us, false, false); - } else { - BigInteger bidist = HashDistance.getDistance(us, ri.getHash()); - dist = biLog2(bidist); - } - if (buf != null && dist < avgMinDist) { + double dist = renderRouterInfo(buf, ri, us, false, false); + if (dist < MIN_CLOSE) + break; + if (dist < avgMinDist) { if (i == 0) { //buf.append("

Not to worry, but above router is closer than average minimum distance " + fmt.format(avgMinDist) + "

"); } else if (i == 1) { @@ -922,22 +1030,17 @@ public class SybilRenderer { median = dist; else if (i == medIdx + 1 && isEven) median = (median + dist) / 2; - double point = MIN_CLOSE - dist; - if (point > 0) { - point *= OUR_KEY_FACTOR; - addPoints(points, ri.getHash(), point, "Very close (" + fmt.format(dist) + ") to our key " + usName + ": " + us.toBase64()); - } - if (i >= MAX - 1) - break; - } - if (buf != null) { - double avg = tot / count; - buf.append("

Totals for " + count + " floodfills:  MIN: " + fmt.format(min) + "  AVG: " + - fmt.format(avg) + "  MEDIAN: " + fmt.format(median) + "  MAX: " + fmt.format(max) + "

\n"); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); } + double avg = tot / count; + buf.append("

Totals for " + count + + " floodfills:  MIN: " + fmt.format(min) + + "  AVG: " + fmt.format(avg) + + "  MEDIAN: " + fmt.format(median) + + "  MAX: " + fmt.format(max) + + "

\n"); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); } /**