package com.subgraph.orchid.circuits.hs;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.subgraph.orchid.ConsensusDocument;
import com.subgraph.orchid.Directory;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.crypto.TorRandom;
import com.subgraph.orchid.data.HexDigest;
public class HSDirectories {
private final static int DIR_CLUSTER_SZ = 3;
private final Directory directory;
private final TorRandom random;
private ConsensusDocument currentConsensus;
private List<Router> hsDirectories;
HSDirectories(Directory directory) {
this.directory = directory;
this.hsDirectories = new ArrayList<Router>();
this.random = new TorRandom();
}
List<HSDescriptorDirectory> getDirectoriesForHiddenService(HiddenService hs) {
final List<HSDescriptorDirectory> dirs = new ArrayList<HSDescriptorDirectory>(2 * DIR_CLUSTER_SZ);
for(HexDigest id: hs.getAllCurrentDescriptorIds()) {
for(Router r: getDirectoriesForDescriptorId(id)) {
dirs.add(new HSDescriptorDirectory(id, r));
}
}
return dirs;
}
private List<Router> getDirectoriesForDescriptorId(HexDigest descriptorId) {
final String hexId = descriptorId.toString();
refreshFromDirectory();
final int idx = getIndexForDescriptorId(hexId);
return selectDirectoriesAtIndex(idx);
}
private int getIndexForDescriptorId(String hexId) {
for(int i = 0; i < hsDirectories.size(); i++) {
String routerId = getHexIdForIndex(i);
if(routerId.compareTo(hexId) > 0) {
return i;
}
}
return 0;
}
private String getHexIdForIndex(int idx) {
final Router r = hsDirectories.get(idx);
return r.getIdentityHash().toString();
}
private List<Router> selectDirectoriesAtIndex(int idx) {
if(idx < 0 || idx >= hsDirectories.size()) {
throw new IllegalArgumentException("idx = "+ idx);
}
if(hsDirectories.size() < DIR_CLUSTER_SZ) {
throw new IllegalStateException();
}
final List<Router> dirs = new ArrayList<Router>(DIR_CLUSTER_SZ);
for(int i = 0; i < DIR_CLUSTER_SZ; i++) {
dirs.add(hsDirectories.get(idx));
idx += 1;
if(idx == hsDirectories.size()) {
idx = 0;
}
}
randomShuffle(dirs);
return dirs;
}
private void refreshFromDirectory() {
ConsensusDocument consensus = directory.getCurrentConsensusDocument();
if(currentConsensus == consensus) {
return;
}
currentConsensus = consensus;
hsDirectories.clear();
for(Router r: directory.getAllRouters()) {
if(r.isHSDirectory()) {
hsDirectories.add(r);
}
}
Collections.sort(hsDirectories, new Comparator<Router>() {
public int compare(Router r1, Router r2) {
final String s1 = r1.getIdentityHash().toString();
final String s2 = r2.getIdentityHash().toString();
return s1.compareTo(s2);
}
});
}
private void randomShuffle(List<Router> dirs) {
for(int i = 0; i < dirs.size(); i++) {
swap(dirs, i, random.nextInt(dirs.size()));
}
}
private void swap(List<Router> dirs, int idx1, int idx2) {
if(idx1 != idx2) {
final Router r1 = dirs.get(idx1);
final Router r2 = dirs.get(idx2);
dirs.set(idx1, r2);
dirs.set(idx2, r1);
}
}
}