forked from I2P_Developers/i2p.i2p
Tunnels: Switch from SHA256 to SipHash for arbitrary deterministic sort
of peers. For efficiency. ref: http://zzz.i2p/topics/3082 thx: jogger
This commit is contained in:
@@ -17,6 +17,7 @@ import java.util.TreeSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import net.i2p.crypto.SipHashInline;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
@@ -1352,6 +1353,12 @@ public class ProfileOrganizer {
|
||||
private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude,
|
||||
Set<Hash> matches, Hash randomKey, Slice subTierMode) {
|
||||
List<Hash> all = new ArrayList<Hash>(peers.keySet());
|
||||
byte[] rk = randomKey.getData();
|
||||
// we use the first half of the random key here,
|
||||
// the second half is used in TunnelPeerSelector.
|
||||
long k0 = DataHelper.fromLong8(rk, 0);
|
||||
long k1 = DataHelper.fromLong8(rk, 8);
|
||||
|
||||
// use RandomIterator to avoid shuffling the whole thing
|
||||
for (Iterator<Hash> iter = new RandomIterator<Hash>(all); (matches.size() < howMany) && iter.hasNext(); ) {
|
||||
Hash peer = iter.next();
|
||||
@@ -1361,7 +1368,7 @@ public class ProfileOrganizer {
|
||||
continue;
|
||||
if (_us.equals(peer))
|
||||
continue;
|
||||
int subTier = getSubTier(peer, randomKey);
|
||||
int subTier = getSubTier(peer, k0, k1);
|
||||
if ((subTier & subTierMode.mask) != subTierMode.val)
|
||||
continue;
|
||||
boolean ok = isSelectable(peer);
|
||||
@@ -1375,15 +1382,13 @@ public class ProfileOrganizer {
|
||||
/**
|
||||
* Implement a random, deterministic split into 4 groups that cannot be predicted by
|
||||
* others.
|
||||
*
|
||||
* @param k0 random key part 1
|
||||
* @param k1 random key part 2
|
||||
* @return 0-3
|
||||
*/
|
||||
private int getSubTier(Hash peer, Hash randomKey) {
|
||||
// input is first 64 bytes; output is last 32 bytes
|
||||
byte[] data = new byte[96];
|
||||
System.arraycopy(peer.getData(), 0, data, 0, 32);
|
||||
System.arraycopy(randomKey.getData(), 0, data, 32, 32);
|
||||
_context.sha().calculateHash(data, 0, 64, data, 64);
|
||||
return data[64] & 0x03;
|
||||
private int getSubTier(Hash peer, long k0, long k1) {
|
||||
return ((int) SipHashInline.hash24(k0, k1, peer.getData())) & 0x03;
|
||||
}
|
||||
|
||||
public boolean isSelectable(Hash peer) {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package net.i2p.router.tunnel.pool;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -15,6 +14,7 @@ import java.util.StringTokenizer;
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.crypto.SipHashInline;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
@@ -26,7 +26,6 @@ import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.transport.TransportUtil;
|
||||
import net.i2p.router.util.HashDistance;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.VersionComparator;
|
||||
@@ -653,34 +652,40 @@ public abstract class TunnelPeerSelector extends ConnectChecker {
|
||||
* to maximize his chances of those two routers being at opposite
|
||||
* ends of a tunnel.
|
||||
*
|
||||
* Previous:
|
||||
* Previous Previous:
|
||||
* d(l, h) - d(r, h)
|
||||
*
|
||||
* Now:
|
||||
* Previous:
|
||||
* d((H(l+h), h) - d(H(r+h), h)
|
||||
*
|
||||
* Now:
|
||||
* SipHash using h to generate the SipHash keys
|
||||
* then siphash(l) - siphash(r)
|
||||
*/
|
||||
private static class HashComparator implements Comparator<Hash>, Serializable {
|
||||
private final Hash _hash, tmp;
|
||||
private final byte[] data;
|
||||
private final long k0, k1;
|
||||
|
||||
/** not thread safe */
|
||||
/**
|
||||
* not thread safe
|
||||
*
|
||||
* @param h container for sort keys, not used as a Hash
|
||||
*/
|
||||
private HashComparator(Hash h) {
|
||||
_hash = h;
|
||||
tmp = new Hash(new byte[Hash.HASH_LENGTH]);
|
||||
data = new byte[2*Hash.HASH_LENGTH];
|
||||
System.arraycopy(_hash.getData(), 0, data, Hash.HASH_LENGTH, Hash.HASH_LENGTH);
|
||||
byte[] b = h.getData();
|
||||
// we use the first half of the random key in ProfileOrganizer.getSubTier(),
|
||||
// so use the last half here
|
||||
k0 = DataHelper.fromLong8(b, 16);
|
||||
k1 = DataHelper.fromLong8(b, 24);
|
||||
}
|
||||
|
||||
public int compare(Hash l, Hash r) {
|
||||
System.arraycopy(l.getData(), 0, data, 0, Hash.HASH_LENGTH);
|
||||
byte[] tb = tmp.getData();
|
||||
// don't use caching version of calculateHash()
|
||||
SHA256Generator.getInstance().calculateHash(data, 0, 2*Hash.HASH_LENGTH, tb, 0);
|
||||
BigInteger ll = HashDistance.getDistance(_hash, tmp);
|
||||
System.arraycopy(r.getData(), 0, data, 0, Hash.HASH_LENGTH);
|
||||
SHA256Generator.getInstance().calculateHash(data, 0, 2*Hash.HASH_LENGTH, tb, 0);
|
||||
BigInteger rr = HashDistance.getDistance(_hash, tmp);
|
||||
return ll.compareTo(rr);
|
||||
long lh = SipHashInline.hash24(k0, k1, l.getData());
|
||||
long rh = SipHashInline.hash24(k0, k1, r.getData());
|
||||
if (lh > rh)
|
||||
return 1;
|
||||
if (lh < rh)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user