[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [onionoo/master] Add "effective_family" field to details documents.
commit d3590d9bd48c0d8980360fb476bac9cc7a9f15bd
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Fri Jul 3 10:20:34 2015 +0200
Add "effective_family" field to details documents.
Implements #16276.
---
build.xml | 2 +-
.../torproject/onionoo/docs/DetailsDocument.java | 8 ++
.../org/torproject/onionoo/docs/DetailsStatus.java | 8 ++
.../org/torproject/onionoo/docs/DocumentStore.java | 2 +-
.../org/torproject/onionoo/docs/NodeStatus.java | 34 ++++++-
.../torproject/onionoo/docs/SummaryDocument.java | 17 +++-
.../org/torproject/onionoo/server/NodeIndexer.java | 30 ++++--
.../torproject/onionoo/server/ResponseBuilder.java | 4 +-
.../onionoo/updater/NodeDetailsStatusUpdater.java | 102 ++++++++++++++++----
.../onionoo/writer/DetailsDocumentWriter.java | 8 ++
.../onionoo/writer/SummaryDocumentWriter.java | 3 +-
.../onionoo/server/ResourceServletTest.java | 15 ++-
web/protocol.html | 15 +++
13 files changed, 209 insertions(+), 39 deletions(-)
diff --git a/build.xml b/build.xml
index b10987b..75dfe1c 100644
--- a/build.xml
+++ b/build.xml
@@ -1,6 +1,6 @@
<project default="dist" name="onionoo" basedir=".">
- <property name="onionoo.protocol.version" value="2.3"/>
+ <property name="onionoo.protocol.version" value="2.4"/>
<property name="release.version"
value="${onionoo.protocol.version}.0"/>
<property name="javasources" value="src/main/java"/>
diff --git a/src/main/java/org/torproject/onionoo/docs/DetailsDocument.java b/src/main/java/org/torproject/onionoo/docs/DetailsDocument.java
index d4efdc0..2b65e50 100644
--- a/src/main/java/org/torproject/onionoo/docs/DetailsDocument.java
+++ b/src/main/java/org/torproject/onionoo/docs/DetailsDocument.java
@@ -289,6 +289,14 @@ public class DetailsDocument extends Document {
return this.family;
}
+ private SortedSet<String> effective_family;
+ public void setEffectiveFamily(SortedSet<String> effectiveFamily) {
+ this.effective_family = effectiveFamily;
+ }
+ public SortedSet<String> getEffectiveFamily() {
+ return this.effective_family;
+ }
+
private Float consensus_weight_fraction;
public void setConsensusWeightFraction(Float consensusWeightFraction) {
if (consensusWeightFraction == null ||
diff --git a/src/main/java/org/torproject/onionoo/docs/DetailsStatus.java b/src/main/java/org/torproject/onionoo/docs/DetailsStatus.java
index fe46416..09f0824 100644
--- a/src/main/java/org/torproject/onionoo/docs/DetailsStatus.java
+++ b/src/main/java/org/torproject/onionoo/docs/DetailsStatus.java
@@ -110,6 +110,14 @@ public class DetailsStatus extends Document {
return this.family;
}
+ private SortedSet<String> effective_family;
+ public void setEffectiveFamily(SortedSet<String> effectiveFamily) {
+ this.effective_family = effectiveFamily;
+ }
+ public SortedSet<String> getEffectiveFamily() {
+ return this.effective_family;
+ }
+
private Map<String, List<String>> exit_policy_v6_summary;
public void setExitPolicyV6Summary(
Map<String, List<String>> exitPolicyV6Summary) {
diff --git a/src/main/java/org/torproject/onionoo/docs/DocumentStore.java b/src/main/java/org/torproject/onionoo/docs/DocumentStore.java
index 3be6fdb..8e2162c 100644
--- a/src/main/java/org/torproject/onionoo/docs/DocumentStore.java
+++ b/src/main/java/org/torproject/onionoo/docs/DocumentStore.java
@@ -422,7 +422,7 @@ public class DocumentStore {
SummaryDocument summaryDocument = new SummaryDocument(isRelay,
nickname, fingerprint, addresses, lastSeenMillis, running,
relayFlags, consensusWeight, countryCode, firstSeenMillis,
- aSNumber, contact, family);
+ aSNumber, contact, family, family);
return summaryDocument;
}
diff --git a/src/main/java/org/torproject/onionoo/docs/NodeStatus.java b/src/main/java/org/torproject/onionoo/docs/NodeStatus.java
index 0985491..51fc678 100644
--- a/src/main/java/org/torproject/onionoo/docs/NodeStatus.java
+++ b/src/main/java/org/torproject/onionoo/docs/NodeStatus.java
@@ -301,6 +301,16 @@ public class NodeStatus extends Document {
return this.lastRdnsLookup;
}
+ /* Computed effective family */
+
+ private String[] effectiveFamily;
+ public void setEffectiveFamily(SortedSet<String> effectiveFamily) {
+ this.effectiveFamily = collectionToStringArray(effectiveFamily);
+ }
+ public SortedSet<String> getEffectiveFamily() {
+ return stringArrayToSortedSet(this.effectiveFamily);
+ }
+
/* Constructor and (de-)serialization methods: */
public NodeStatus(String fingerprint) {
@@ -402,8 +412,17 @@ public class NodeStatus extends Document {
nodeStatus.setRecommendedVersion(parts[21].equals("true"));
}
if (!parts[22].equals("null")) {
- nodeStatus.setFamilyFingerprints(new TreeSet<String>(
- Arrays.asList(parts[22].split(";"))));
+ SortedSet<String> familyFingerprints = new TreeSet<String>();
+ for (String familyMember : parts[22].split("[;:]")) {
+ if (familyMember.length() > 0) {
+ familyFingerprints.add(familyMember);
+ }
+ }
+ nodeStatus.setFamilyFingerprints(familyFingerprints);
+ if (parts[22].contains(":")) {
+ nodeStatus.setEffectiveFamily(new TreeSet<String>(
+ Arrays.asList(parts[22].split(":", 2)[1].split(";"))));
+ }
}
return nodeStatus;
} catch (NumberFormatException e) {
@@ -464,7 +483,16 @@ public class NodeStatus extends Document {
sb.append("\t" + (this.contact != null ? this.contact : ""));
sb.append("\t" + (this.recommendedVersion == null ? "null" :
this.recommendedVersion ? "true" : "false"));
- sb.append("\t" + StringUtils.join(this.familyFingerprints, ";"));
+ if (this.effectiveFamily != null && this.effectiveFamily.length > 0) {
+ SortedSet<String> mutual = this.getEffectiveFamily();
+ SortedSet<String> notMutual = new TreeSet<String>(
+ this.getFamilyFingerprints());
+ notMutual.removeAll(mutual);
+ sb.append("\t" + StringUtils.join(notMutual, ";") + ":"
+ + StringUtils.join(mutual, ";"));
+ } else {
+ sb.append("\t" + StringUtils.join(this.familyFingerprints, ";"));
+ }
return sb.toString();
}
}
diff --git a/src/main/java/org/torproject/onionoo/docs/SummaryDocument.java b/src/main/java/org/torproject/onionoo/docs/SummaryDocument.java
index 8e325f3..ae7202b 100644
--- a/src/main/java/org/torproject/onionoo/docs/SummaryDocument.java
+++ b/src/main/java/org/torproject/onionoo/docs/SummaryDocument.java
@@ -185,6 +185,9 @@ public class SummaryDocument extends Document {
return this.c;
}
+ /* This attribute can go away once all Onionoo services had their hourly
+ * updater write effective families to summary documents at least once.
+ * Remove this code after September 8, 2015. */
private String[] ff;
public void setFamilyFingerprints(
SortedSet<String> familyFingerprints) {
@@ -194,11 +197,22 @@ public class SummaryDocument extends Document {
return this.stringArrayToSortedSet(this.ff);
}
+ private String[] ef;
+ public void setEffectiveFamily(SortedSet<String> effectiveFamily) {
+ this.ef = this.collectionToStringArray(effectiveFamily);
+ }
+ public SortedSet<String> getEffectiveFamily() {
+ return this.stringArrayToSortedSet(this.ef);
+ }
+
+ /* The familyFingerprints parameter can go away after September 8, 2015.
+ * See above. */
public SummaryDocument(boolean isRelay, String nickname,
String fingerprint, List<String> addresses, long lastSeenMillis,
boolean running, SortedSet<String> relayFlags, long consensusWeight,
String countryCode, long firstSeenMillis, String aSNumber,
- String contact, SortedSet<String> familyFingerprints) {
+ String contact, SortedSet<String> familyFingerprints,
+ SortedSet<String> effectiveFamily) {
this.setRelay(isRelay);
this.setNickname(nickname);
this.setFingerprint(fingerprint);
@@ -212,6 +226,7 @@ public class SummaryDocument extends Document {
this.setASNumber(aSNumber);
this.setContact(contact);
this.setFamilyFingerprints(familyFingerprints);
+ this.setEffectiveFamily(effectiveFamily);
}
}
diff --git a/src/main/java/org/torproject/onionoo/server/NodeIndexer.java b/src/main/java/org/torproject/onionoo/server/NodeIndexer.java
index 5788d4e..347996b 100644
--- a/src/main/java/org/torproject/onionoo/server/NodeIndexer.java
+++ b/src/main/java/org/torproject/onionoo/server/NodeIndexer.java
@@ -1,7 +1,6 @@
package org.torproject.onionoo.server;
import java.io.File;
-import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -19,7 +18,6 @@ import javax.servlet.ServletContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import org.torproject.onionoo.docs.DocumentStore;
import org.torproject.onionoo.docs.DocumentStoreFactory;
import org.torproject.onionoo.docs.SummaryDocument;
@@ -160,6 +158,11 @@ public class NodeIndexer implements ServletContextListener, Runnable {
}
Time time = TimeFactory.getTime();
List<String> orderRelaysByConsensusWeight = new ArrayList<String>();
+ /* This variable can go away once all Onionoo services had their
+ * hourly updater write effective families to summary documents at
+ * least once. Remove this code after September 8, 2015. */
+ SortedMap<String, Set<String>> computedEffectiveFamilies =
+ new TreeMap<String, Set<String>>();
for (SummaryDocument entry : currentRelays) {
String fingerprint = entry.getFingerprint().toUpperCase();
String hashedFingerprint = entry.getHashedFingerprint().
@@ -196,8 +199,16 @@ public class NodeIndexer implements ServletContextListener, Runnable {
newRelaysByFlag.get(flagLowerCase).add(fingerprint);
newRelaysByFlag.get(flagLowerCase).add(hashedFingerprint);
}
- if (entry.getFamilyFingerprints() != null) {
- newRelaysByFamily.put(fingerprint, entry.getFamilyFingerprints());
+ /* This condition can go away once all Onionoo services had their
+ * hourly updater write effective families to summary documents at
+ * least once. Remove this code after September 8, 2015. */
+ if (entry.getFamilyFingerprints() != null &&
+ !entry.getFamilyFingerprints().isEmpty()) {
+ computedEffectiveFamilies.put(fingerprint,
+ entry.getFamilyFingerprints());
+ }
+ if (entry.getEffectiveFamily() != null) {
+ newRelaysByFamily.put(fingerprint, entry.getEffectiveFamily());
}
int daysSinceFirstSeen = (int) ((time.currentTimeMillis()
- entry.getFirstSeenMillis()) / ONE_DAY);
@@ -229,18 +240,21 @@ public class NodeIndexer implements ServletContextListener, Runnable {
for (String relay : orderRelaysByConsensusWeight) {
newRelaysByConsensusWeight.add(relay.split(" ")[1]);
}
+ /* This loop can go away once all Onionoo services had their hourly
+ * updater write effective families to summary documents at least
+ * once. Remove this code after September 8, 2015. */
for (Map.Entry<String, Set<String>> e :
- newRelaysByFamily.entrySet()) {
+ computedEffectiveFamilies.entrySet()) {
String fingerprint = e.getKey();
Set<String> inMutualFamilyRelation = new HashSet<String>();
for (String otherFingerprint : e.getValue()) {
- if (newRelaysByFamily.containsKey(otherFingerprint) &&
- newRelaysByFamily.get(otherFingerprint).contains(
+ if (computedEffectiveFamilies.containsKey(otherFingerprint) &&
+ computedEffectiveFamilies.get(otherFingerprint).contains(
fingerprint)) {
inMutualFamilyRelation.add(otherFingerprint);
}
}
- e.getValue().retainAll(inMutualFamilyRelation);
+ newRelaysByFamily.put(fingerprint, inMutualFamilyRelation);
}
for (SummaryDocument entry : currentBridges) {
String hashedFingerprint = entry.getFingerprint().toUpperCase();
diff --git a/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java b/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
index af0f67e..c7bfd65 100644
--- a/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
+++ b/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
@@ -70,7 +70,7 @@ public class ResponseBuilder {
return this.charsWritten;
}
- private static final String PROTOCOL_VERSION = "2.3";
+ private static final String PROTOCOL_VERSION = "2.4";
private static final String NEXT_MAJOR_VERSION_SCHEDULED = null;
@@ -267,6 +267,8 @@ public class ResponseBuilder {
dd.setHibernating(detailsDocument.getHibernating());
} else if (field.equals("transports")) {
dd.setTransports(detailsDocument.getTransports());
+ } else if (field.equals("effective_family")) {
+ dd.setEffectiveFamily(detailsDocument.getEffectiveFamily());
}
}
/* Don't escape HTML characters, like < and >, contained in
diff --git a/src/main/java/org/torproject/onionoo/updater/NodeDetailsStatusUpdater.java b/src/main/java/org/torproject/onionoo/updater/NodeDetailsStatusUpdater.java
index be2bd9a..4fb0143 100644
--- a/src/main/java/org/torproject/onionoo/updater/NodeDetailsStatusUpdater.java
+++ b/src/main/java/org/torproject/onionoo/updater/NodeDetailsStatusUpdater.java
@@ -56,14 +56,14 @@ import org.torproject.onionoo.util.TimeFactory;
* are not loaded from disk before the parse step in order to save
* memory for parsed descriptors.
* 3. Perform reverse DNS lookups, Look up relay IP addresses in a
- * GeoIP database, and calculate path selection probabilities.
- * Update node statuses accordingly.
+ * GeoIP database, calculate path selection probabilities, and
+ * compute effective families, and update node statuses accordingly.
* 4. Retrieve details statuses corresponding to nodes that have been
* changed since the start of the update process, possibly update the
* node statuses with contents from newly parsed descriptors, update
- * details statuses with results from lookup operations and new path
- * selection probabilities, and store details statuses and node
- * statuses back to disk.
+ * details statuses with results from lookup operations, new path
+ * selection probabilities, and effective families, and store details
+ * statuses and node statuses back to disk.
*/
public class NodeDetailsStatusUpdater implements DescriptorListener,
StatusUpdater {
@@ -140,6 +140,9 @@ public class NodeDetailsStatusUpdater implements DescriptorListener,
}
}
+ private Map<String, SortedSet<String>> familyFingerprints =
+ new HashMap<String, SortedSet<String>>();
+
private void processRelayServerDescriptor(
ServerDescriptor descriptor) {
String fingerprint = descriptor.getFingerprint();
@@ -170,6 +173,16 @@ public class NodeDetailsStatusUpdater implements DescriptorListener,
detailsStatus.setContact(descriptor.getContact());
detailsStatus.setPlatform(descriptor.getPlatform());
detailsStatus.setFamily(descriptor.getFamilyEntries());
+ if (descriptor.getFamilyEntries() != null) {
+ SortedSet<String> noPrefixUpperCase = new TreeSet<String>();
+ for (String familyMember : descriptor.getFamilyEntries()) {
+ if (familyMember.startsWith("$") && familyMember.length() >= 41) {
+ noPrefixUpperCase.add(
+ familyMember.substring(1, 41).toUpperCase());
+ }
+ }
+ this.familyFingerprints.put(fingerprint, noPrefixUpperCase);
+ }
if (descriptor.getIpv6DefaultPolicy() != null &&
(descriptor.getIpv6DefaultPolicy().equals("accept") ||
descriptor.getIpv6DefaultPolicy().equals("reject")) &&
@@ -354,6 +367,8 @@ public class NodeDetailsStatusUpdater implements DescriptorListener,
log.info("Looked up cities and ASes");
this.calculatePathSelectionProbabilities();
log.info("Calculated path selection probabilities");
+ this.computeEffectiveFamilies();
+ log.info("Computed effective families");
this.finishReverseDomainNameLookups();
log.info("Finished reverse domain name lookups");
this.updateNodeDetailsStatuses();
@@ -449,6 +464,10 @@ public class NodeDetailsStatusUpdater implements DescriptorListener,
}
updatedNodeStatus.setLastRdnsLookup(
nodeStatus.getLastRdnsLookup());
+ updatedNodeStatus.setFamilyFingerprints(
+ nodeStatus.getFamilyFingerprints());
+ updatedNodeStatus.setEffectiveFamily(
+ nodeStatus.getEffectiveFamily());
} else {
updatedNodeStatus = nodeStatus;
this.knownNodes.put(fingerprint, nodeStatus);
@@ -471,6 +490,19 @@ public class NodeDetailsStatusUpdater implements DescriptorListener,
}
}
}
+ /* Update family fingerprints in known nodes with any fingerprints we
+ * learned when parsing server descriptors in this run. These are
+ * guaranteed to come from more recent server descriptors, so it's
+ * safe to override whatever is in node statuses. */
+ for (Map.Entry<String, NodeStatus> e : this.knownNodes.entrySet()) {
+ String fingerprint = e.getKey();
+ if (this.familyFingerprints.containsKey(fingerprint)) {
+ NodeStatus nodeStatus = e.getValue();
+ nodeStatus.setFamilyFingerprints(
+ this.familyFingerprints.get(fingerprint));
+ }
+ }
+ this.familyFingerprints.clear();
}
/* Step 3: perform lookups and calculate path selection
@@ -617,6 +649,52 @@ public class NodeDetailsStatusUpdater implements DescriptorListener,
}
}
+ private void computeEffectiveFamilies() {
+ SortedMap<String, SortedSet<String>> declaredFamilies =
+ new TreeMap<String, SortedSet<String>>();
+ for (String fingerprint : this.currentRelays) {
+ NodeStatus nodeStatus = this.knownNodes.get(fingerprint);
+ if (nodeStatus != null &&
+ nodeStatus.getFamilyFingerprints() != null &&
+ !nodeStatus.getFamilyFingerprints().isEmpty()) {
+ declaredFamilies.put(fingerprint,
+ nodeStatus.getFamilyFingerprints());
+ }
+ }
+ SortedMap<String, SortedSet<String>> effectiveFamilies =
+ new TreeMap<String, SortedSet<String>>();
+ for (Map.Entry<String, SortedSet<String>> e :
+ declaredFamilies.entrySet()) {
+ String fingerprint = e.getKey();
+ SortedSet<String> declaredFamily = e.getValue();
+ SortedSet<String> effectiveFamily = new TreeSet<String>();
+ for (String declaredFamilyMember : declaredFamily) {
+ if (declaredFamilies.containsKey(declaredFamilyMember) &&
+ declaredFamilies.get(declaredFamilyMember).contains(
+ fingerprint)) {
+ effectiveFamily.add(declaredFamilyMember);
+ }
+ }
+ if (!effectiveFamily.isEmpty()) {
+ effectiveFamilies.put(fingerprint, effectiveFamily);
+ }
+ }
+ for (String fingerprint : this.currentRelays) {
+ NodeStatus nodeStatus = this.knownNodes.get(fingerprint);
+ if (nodeStatus == null) {
+ continue;
+ }
+ if (effectiveFamilies.containsKey(fingerprint)) {
+ nodeStatus.setEffectiveFamily(effectiveFamilies.get(fingerprint));
+ this.updatedNodes.add(fingerprint);
+ } else if (nodeStatus.getEffectiveFamily() != null ||
+ !nodeStatus.getEffectiveFamily().isEmpty()) {
+ nodeStatus.setEffectiveFamily(null);
+ this.updatedNodes.add(fingerprint);
+ }
+ }
+ }
+
private long startedRdnsLookups = -1L;
private SortedMap<String, String> rdnsLookupResults =
@@ -681,19 +759,7 @@ public class NodeDetailsStatusUpdater implements DescriptorListener,
nodeStatus.getOrAddresses());
nodeStatus.setExitAddresses(exitAddressesWithoutOrAddresses);
- if (detailsStatus.getFamily() != null &&
- !detailsStatus.getFamily().isEmpty()) {
- SortedSet<String> familyFingerprints = new TreeSet<String>();
- for (String familyMember : detailsStatus.getFamily()) {
- if (familyMember.startsWith("$") &&
- familyMember.length() == 41) {
- familyFingerprints.add(familyMember.substring(1));
- }
- }
- if (!familyFingerprints.isEmpty()) {
- nodeStatus.setFamilyFingerprints(familyFingerprints);
- }
- }
+ detailsStatus.setEffectiveFamily(nodeStatus.getEffectiveFamily());
if (this.geoIpLookupResults.containsKey(fingerprint)) {
LookupResult lookupResult = this.geoIpLookupResults.get(
diff --git a/src/main/java/org/torproject/onionoo/writer/DetailsDocumentWriter.java b/src/main/java/org/torproject/onionoo/writer/DetailsDocumentWriter.java
index 1a1ddc3..152b4cb 100644
--- a/src/main/java/org/torproject/onionoo/writer/DetailsDocumentWriter.java
+++ b/src/main/java/org/torproject/onionoo/writer/DetailsDocumentWriter.java
@@ -112,6 +112,14 @@ public class DetailsDocumentWriter implements DocumentWriter {
detailsDocument.setContact(detailsStatus.getContact());
detailsDocument.setPlatform(detailsStatus.getPlatform());
detailsDocument.setFamily(detailsStatus.getFamily());
+ if (detailsStatus.getEffectiveFamily() != null &&
+ !detailsStatus.getEffectiveFamily().isEmpty()) {
+ SortedSet<String> effectiveFamily = new TreeSet<String>();
+ for (String familyMember : detailsStatus.getEffectiveFamily()) {
+ effectiveFamily.add("$" + familyMember);
+ }
+ detailsDocument.setEffectiveFamily(effectiveFamily);
+ }
detailsDocument.setExitPolicyV6Summary(
detailsStatus.getExitPolicyV6Summary());
detailsDocument.setHibernating(detailsStatus.getHibernating());
diff --git a/src/main/java/org/torproject/onionoo/writer/SummaryDocumentWriter.java b/src/main/java/org/torproject/onionoo/writer/SummaryDocumentWriter.java
index 6406a28..ddb3003 100644
--- a/src/main/java/org/torproject/onionoo/writer/SummaryDocumentWriter.java
+++ b/src/main/java/org/torproject/onionoo/writer/SummaryDocumentWriter.java
@@ -86,10 +86,11 @@ public class SummaryDocumentWriter implements DocumentWriter {
String contact = nodeStatus.getContact();
SortedSet<String> familyFingerprints =
nodeStatus.getFamilyFingerprints();
+ SortedSet<String> effectiveFamily = nodeStatus.getEffectiveFamily();
SummaryDocument summaryDocument = new SummaryDocument(isRelay,
nickname, fingerprint, addresses, lastSeenMillis, running,
relayFlags, consensusWeight, countryCode, firstSeenMillis,
- aSNumber, contact, familyFingerprints);
+ aSNumber, contact, familyFingerprints, effectiveFamily);
if (this.documentStore.store(summaryDocument, fingerprint)) {
this.writtenDocuments++;
};
diff --git a/src/test/java/org/torproject/onionoo/server/ResourceServletTest.java b/src/test/java/org/torproject/onionoo/server/ResourceServletTest.java
index c6ef9c8..f52413a 100644
--- a/src/test/java/org/torproject/onionoo/server/ResourceServletTest.java
+++ b/src/test/java/org/torproject/onionoo/server/ResourceServletTest.java
@@ -128,7 +128,9 @@ public class ResourceServletTest {
"torkaz <klaus dot zufall at gmx dot de> "
+ "<fb-token:np5_g_83jmf=>", new TreeSet<String>(Arrays.asList(
new String[] { "001C13B3A55A71B977CA65EC85539D79C653A3FC",
- "0025C136C1F3A9EEFE2AE3F918F03BFA21B5070B" })));
+ "0025C136C1F3A9EEFE2AE3F918F03BFA21B5070B" })),
+ new TreeSet<String>(Arrays.asList(
+ new String[] { "001C13B3A55A71B977CA65EC85539D79C653A3FC" })));
org.torproject.onionoo.docs.SummaryDocument relayFerrari458 =
new org.torproject.onionoo.docs.SummaryDocument(true, "Ferrari458",
"001C13B3A55A71B977CA65EC85539D79C653A3FC", Arrays.asList(
@@ -138,6 +140,8 @@ public class ResourceServletTest {
"Running", "V2Dir", "Valid" })), 1140L, "us",
DateTimeHelper.parse("2013-02-12 16:00:00"), "AS7922", null,
new TreeSet<String>(Arrays.asList(new String[] {
+ "000C5F55BD4814B917CC474BD537F1A3B33CCE2A" })),
+ new TreeSet<String>(Arrays.asList(new String[] {
"000C5F55BD4814B917CC474BD537F1A3B33CCE2A" })));
org.torproject.onionoo.docs.SummaryDocument relayTimMayTribute =
new org.torproject.onionoo.docs.SummaryDocument(true, "TimMayTribute",
@@ -149,7 +153,7 @@ public class ResourceServletTest {
DateTimeHelper.parse("2013-04-16 18:00:00"), "AS6830",
"1024D/51E2A1C7 steven j. murdoch "
+ "<tor+steven.murdoch@xxxxxxxxxxxx> <fb-token:5sr_k_zs2wm=>",
- new TreeSet<String>());
+ new TreeSet<String>(), new TreeSet<String>());
org.torproject.onionoo.docs.SummaryDocument bridgeec2bridgercc7f31fe =
new org.torproject.onionoo.docs.SummaryDocument(false,
"ec2bridgercc7f31fe", "0000831B236DFF73D409AD17B40E2A728A53994F",
@@ -157,7 +161,7 @@ public class ResourceServletTest {
DateTimeHelper.parse("2013-04-21 18:07:03"), false,
new TreeSet<String>(Arrays.asList(new String[] { "Valid" })), -1L,
null, DateTimeHelper.parse("2013-04-20 15:37:04"), null, null,
- null);
+ null, null);
org.torproject.onionoo.docs.SummaryDocument bridgeUnnamed =
new org.torproject.onionoo.docs.SummaryDocument(false, "Unnamed",
"0002D9BDBBC230BD9C78FF502A16E0033EF87E0C", Arrays.asList(
@@ -165,7 +169,7 @@ public class ResourceServletTest {
DateTimeHelper.parse("2013-04-20 17:37:04"), false,
new TreeSet<String>(Arrays.asList(new String[] { "Valid" })), -1L,
null, DateTimeHelper.parse("2013-04-14 07:07:05"), null, null,
- null);
+ null, null);
org.torproject.onionoo.docs.SummaryDocument bridgegummy =
new org.torproject.onionoo.docs.SummaryDocument(false, "gummy",
"1FEDE50ED8DBA1DD9F9165F78C8131E4A44AB756", Arrays.asList(
@@ -173,7 +177,8 @@ public class ResourceServletTest {
DateTimeHelper.parse("2013-04-24 01:07:04"), true,
new TreeSet<String>(Arrays.asList(new String[] { "Running",
"Valid" })), -1L, null,
- DateTimeHelper.parse("2013-01-16 21:07:04"), null, null, null);
+ DateTimeHelper.parse("2013-01-16 21:07:04"), null, null, null,
+ null);
this.relays =
new TreeMap<String, org.torproject.onionoo.docs.SummaryDocument>();
this.relays.put("000C5F55BD4814B917CC474BD537F1A3B33CCE2A",
diff --git a/web/protocol.html b/web/protocol.html
index 00221b0..9f36e4d 100644
--- a/web/protocol.html
+++ b/web/protocol.html
@@ -176,6 +176,8 @@ added "transports" field to bridge details documents on December 8,
2014.</li>
<li><strong>2.3</strong>: Added optional "flags" field to uptime
documents on March 22, 2015.</li>
+<li><strong>2.4</strong>: Added optional "effective_family" field to
+details documents on July 3, 2015.</li>
</ul>
</div> <!-- box -->
@@ -1185,6 +1187,19 @@ found.
</li>
<li>
+<b><font color="blue">effective_family</font></b>
+<code class="typeof">array of strings</code>
+<span class="required-false">optional</span>
+<p>
+Array of $-prefixed fingerprints of relays that are in an effective,
+mutual family relationship with this relay.
+Omitted if empty or if descriptor containing this information cannot be
+found.
+<font color="blue">Added on July 3, 2015.</font>
+</p>
+</li>
+
+<li>
<b>consensus_weight_fraction</b>
<code class="typeof">number</code>
<span class="required-false">optional</span>
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits